Clef uses two separate APIs. The external API is an untrusted set of JSON-RPC methods that can be called by a user. The internal API is a set of JSON-RPC methods that can be called by a UI. The UI could be Clef's native command line interface or a custom UI.
External API
Clef listens to HTTP requests on http.addr:http.port (or to IPC on ipcpath), with the same JSON-RPC standard as Etn-sc. The messages are expected to be JSON-RPC 2.0 standard.
Some of these JSON-RPC calls require user interaction in the Clef terminal. Responses may be delayed significantly or may never be received if a user fails to respond to a confirmation request.
The External API is untrusted: it does not accept credentials, nor does it expect that requests have any authority.
The signer will generate a new private key, encrypt it according to web3 keystore spec and store it in the keystore directory. The client is responsible for creating a backup of the keystore. If the keystore is lost there is no method of retrieving lost accounts.
Arguments
None
Result
address [string]: account address that is derived from the generated key
Signs a transaction and responds with the signed transaction in RLP-encoded and JSON forms. Supports both legacy and EIP-1559-style transactions.
Arguments
transaction object (legacy):
from [address]: account to send the transaction from
to [address]: receiver account. If omitted or 0x, will cause contract creation.
gas [number]: maximum amount of gas to burn
gasPrice [number]: gas price
value [number:optional]: amount of Wei to send with the transaction
data [data:optional]: input data
nonce [number]: account nonce
transaction object (1559):
from [address]: account to send the transaction from
to [address]: receiver account. If omitted or 0x, will cause contract creation.
gas [number]: maximum amount of gas to burn
maxPriorityFeePerGas [number]: maximum priority fee per unit of gas for the transaction
maxFeePerGas [number]: maximum fee per unit of gas for the transaction
value [number:optional]: amount of Wei to send with the transaction
data [data:optional]: input data
nonce [number]: account nonce
method signature [string:optional]
The method signature, if present, is to aid decoding the calldata. Should consist of methodname(paramtype,...), e.g. transfer(uint256,address). The signer may use this data to parse the supplied calldata, and show the user. The data, however, is considered totally untrusted, and reliability is not expected.
Result
raw [data]: signed transaction in RLP encoded form
Clef has one native console-based UI, for operation without any standalone tools. However, there is also an API to communicate with an external UI. To enable that UI, the signer needs to be started with the --stdio-ui option, which allocates stdin / stdout for the UI API.
The internal API methods need to be implemented by a UI listener. By starting the signer with the switch --stdio-ui-test, the signer will invoke all known methods, and expect the UI to respond with denials. This can be used during development to ensure that the API is (at least somewhat) correctly implemented.
All methods in this API use object-based parameters, so that there can be no mixup of parameters: each piece of data is accessed by key.
An example (insecure) proof-of-concept external UI has been implemented in pythonsigner.py.
The model is as follows:
The user starts the UI app (pythonsigner.py).
The UI app starts clef with --stdio-ui, and listens to the process output for confirmation-requests.
clef opens the external HTTP API.
When the signer receives requests, it sends a JSON-RPC request via stdout.
The UI app prompts the user accordingly, and responds to clef.
clef signs (or not), and responds to the original request.
NOTE A slight deviation from json standard is in place: every request and response should be confined to a single line. Whereas the json specification allows for linebreaks, linebreaks should not be used in this communication channel, to make things simpler for both parties.
{"jsonrpc":"2.0","id":1,"method":"ui_approveTx","params": [ {"transaction": {"from":"0x0x694267f14675d7e1b9494fd8d72fefe1755710fa","to":"0x0x07a565b7ed7d7a678680a4c162885bedbb695fe0","gas":"0x333","gasPrice":"0x1","value":"0x0","nonce":"0x0","data":"0x4401a6e40000000000000002000000000000000000000000000000000000000000000012","input":null },"call_info": [ {"type":"WARNING","message":"Invalid checksum on to-address" }, {"type":"WARNING", "message": "Transaction data did not match ABI-interface: WARNING: Supplied data is stuffed with extra data. \nWant 0000000000000002000000000000000000000000000000000000000000000012\nHave 0000000000000000000000000000000000000000000000000000000000000012\nfor method safeSend(address)"
} ],"meta": {"remote":"127.0.0.1:48492","local":"localhost:8550","scheme":"HTTP/1.1" } } ]}
One which has missing to, but with no data:
{"jsonrpc":"2.0","id":3,"method":"ui_approveTx","params": [ {"transaction": {"from":"","to":null,"gas":"0x0","gasPrice":"0x0","value":"0x0","nonce":"0x0","data":null,"input":null },"call_info": [ {"type":"CRITICAL","message":"Tx will create contract with empty code!" } ],"meta": {"remote":"signer binary","local":"main","scheme":"in-proc" } } ]}
ApproveListing / ui_approveListing
Invoked when a request for account listing has been made.
The UI should show the error (a single message) to the user. Does not expect response.
{"jsonrpc":"2.0","id":2,"method":"ui_showError","params": ["Something bad happened!"]}
OnApprovedTx / ui_onApprovedTx
OnApprovedTx is called when a transaction has been approved and signed. The call contains the return value that will be sent to the external caller. The return value from this method is ignored - the reason for having this callback is to allow the ruleset to keep track of approved transactions.
When implementing rate-limited rules, this callback should be used.
TLDR; Use this method to keep track of signed transactions, instead of using the data in ApproveTx.
This method provides the UI with information about what API version the signer uses (both internal and external) as well as build-info and external API, in k/v-form.
Invoked when Clef requires user input (e.g. a password).
Example call:
{"jsonrpc":"2.0","id":1,"method":"ui_onInputRequired","params": [ {"title":"Account password","prompt":"Please enter the password for account 0x694267f14675d7e1b9494fd8d72fefe1755710fa","isPassword":true } ]}