Warning
This is early software.
This smart contract accepts payments in NEAR tokens in exchange for gas funding on non-NEAR foreign chains. Part of the NEAR Multichain effort, it works in conjunction with the MPC recovery service to generate on-chain signatures.
Contract | Testnet ID | Mainnet ID |
---|---|---|
NFT Chain Key | v2.nft.kagi.testnet |
— |
Gas Station | canhazgas.testnet |
— |
This smart contract is a piece of the NEAR Multichain project, which makes NEAR Protocol an effortlessly cross-chain network. This contract accepts EVM transaction request payloads and facilitates the signing, gas funding, and relaying of the signed transactions to their destination chains. It works in conjunction with a few different services, including:
- The MPC recovery service, also called the "MPC signer service", includes a network of trusted MPC signers, which hold keyshares and cooperatively sign transactions on behalf of the MPC network. It also includes an on-chain component, called the "MPC signer contract," which accepts on-chain signature requests and returns signatures computed by the MPC network.
- The multichain relayer server scans this smart contract for signed transaction payloads and emits them to foreign chain RPCs.
- The NFT chain key smart contract allows chain keys to be transferred between NEAR accounts, using the NEP-171 NFT smart contract standard.
Currently, relaying one transaction to a foreign chain requires three transactions. However, NEP-516 (delayed receipts / runtime triggers) will reduce this number to one.
Transaction breakdown:
- The first transaction is a call to the
create_transaction
function. This function accepts an EVM transaction request payload, an NFT chain key token IDtoken_id
(must have been previously approved to the gas station smart contract), and a deposit amount (to pay for gas on the foreign chain) and returns anid
and apending_transactions_count
. - The second transaction is a call to the
sign_next
function. This function accepts theid
returned in step 1 and returns a signed payload. This payload is the gas funding transaction, transferring funds from a paymaster account on the foreign chain to the user's account on the foreign chain. It must be submitted to the foreign chain before the second signed payload. - The third transaction is another call to the
sign_next
function, identical to the one before. This function accepts anid
and returns a signed payload. This payload is the signed user transaction.
Three transactions are required because of the gas restrictions imposed by the protocol. Currently (pre-NEP-516), the MPC signing function requires a lot of gas, so dividing up the signing process into three parts is required to maximize the amount of gas available to each signing call.
Once this service and its supporting services are live, the multichain relayer server will be monitoring this gas station contract and relaying the signed transactions in the proper order as they become available, so it will not be strictly necessary for the users of this contract to ensure that the transactions are properly relayed, unless the user wishes to relay the transactions using their own RPC (e.g. to minimize latency).
Let's say alice.near
has already called create_transaction(..., token_id=..., use_paymaster=true)
on the gas station contract gas-station.near
, and has obtained a transaction sequence id
as a result of that function call.
Next, alice.near
calls gas-station.near::sign_next(id)
. Because this is the first sign_next
call, the contract first generates a paymaster gas funding transaction. However, this is payload is unsigned at first. It is unwise1 to keep private keys on-chain (they would cease to be private), so the contract invokes the NFT chain key contract, which invokes another service, the MPC signing service.
This service allows us to request signatures from a particular private key that the gas station contract controls. The MPC service allows contracts to request a key by "path," which is simply a string. The signing service then uses a combination of the predecessor account ID (like nft-key.near
), the path string provided as a parameter to the signature request, and a few other pieces of information to derive a recoverable signature for the payload that recovers to a stable public key.
In the case of the paymaster transaction, the gas station uses a set of NFT keys that map to known addresses on the foreign chain. These addresses are pre-funded with the native (gas) token for that foreign chain. Thus, when the gas station contract requests signatures for the paymaster transaction payload, the signed transactions are able to manipulate the funds in that foreign account.
In the case of the second, user-provided transaction, the gas station uses an NFT key that the user has previously ckt_approve_call
-ed to the gas station. This means that each transaction requested by alice.near
will receive a signature that recovers to the same public key (foreign address) every time.
Therefore, the call trace for the two sign_next
transactions looks something like this:
alice.near
→gas-station.near::sign_next(id) -> SignedTransaction
gas-station.near
→nft-key.near::sign(payload=gas_funding_tx, token_id=paymaster_token_id) -> Signature
nft-key.near
→mpc-signer.near::sign(payload=gas_funding_tx, path=paymaster_token_id) -> Signature
alice.near
→gas-station.near::sign_next(id) -> SignedTransaction
gas-station.near
→nft-key.near::sign(payload=user_tx, token_id=token_id) -> Signature
nft-key.near
→mpc-signer.near::sign(payload=user_tx, path=token_id) -> Signature
- Rust & Cargo
cargo-make
cargo-near
near-cli-rs
- Initialize the contract with a call to
new
. The owner is initialized as the predecessor of this transaction. All of the following transactions must be called by the owner. - Set up foreign chain configurations with
add_foreign_chain
. - Add paymasters to each foreign chain by transferring NFT keys and then calling
add_paymaster
.
Users who wish to get transactions signed and relayed by this contract and its accompanying infrastructure should perform the following steps:
- Create an NFT chain key.
ckt_approve_call
the NFT chain key to the gas station contract.
- Construct an unsigned transaction payload for the foreign chain they wish to interact with, e.g. Ethereum.
- Call
create_transaction
on this contract, passing in your NFT chain key ID, that payload, and activating theuse_paymaster
toggle in the case that the user wishes to use a paymaster. If the user uses a paymaster, he must attach a sufficient quantity of NEAR (or whatever accepted local asset is configured) tokens to this transaction to pay for the gas + service fee. This function call returns anid
and apending_transactions_count
. - Call
sign_next
, passing in theid
value obtained in the previous step. This transaction should be executed with the maximum allowable quantity of gas (i.e. 300 TGas). This transaction will return a signed payload, part of the sequence of transactions necessary to send the user's transaction to the foreign chain. Repeatpending_transactions_count
times. - Relay each signed payload to the foreign chain RPC in the order they were requested.
This software has undergone the following audits:
- Jacob Lindahl jacob.lindahl@near.org @sudo_build
Footnotes
-
The debug/mock version of this contract does store private keys on-chain (big no-no), making it only suitable for testing. ↩