From 70eff434c6941764bd6cb5dc8b51cf9d665bb3c0 Mon Sep 17 00:00:00 2001 From: s-n-park <146785798+s-n-park@users.noreply.github.com> Date: Tue, 12 Mar 2024 13:53:36 -0700 Subject: [PATCH 1/4] Update big-query.md (#1815) Clarify bigquery example for unique accounts --- docs/bos/queryapi/big-query.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/bos/queryapi/big-query.md b/docs/bos/queryapi/big-query.md index 8422e9b9166..78b379111cf 100644 --- a/docs/bos/queryapi/big-query.md +++ b/docs/bos/queryapi/big-query.md @@ -32,12 +32,13 @@ The [NEAR Public Lakehouse repository](https://github.com/near/near-public-lakeh ### Example Queries -- _How many unique users do I have for my smart contract per day?_ +- _How many unique signers and accounts have interacted with my smart contract per day?_ ```sql SELECT ra.block_date collected_for_day, - COUNT(DISTINCT t.signer_account_id) as total + COUNT(DISTINCT t.signer_account_id) as total_signers, + COUNT(DISTINCT ra.receipt_predecessor_account_id) as total_accounts FROM `bigquery-public-data.crypto_near_mainnet_us.receipt_actions` ra JOIN `bigquery-public-data.crypto_near_mainnet_us.receipt_origin_transaction` ro ON ro.receipt_id = ra.receipt_id JOIN `bigquery-public-data.crypto_near_mainnet_us.transactions` t ON ro.originated_from_transaction_hash = t.transaction_hash From 545f2367379a902da4d1cb4b18e385447d1e9f97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dj8yf0=CE=BCl?= <26653921+dj8yfo@users.noreply.github.com> Date: Wed, 13 Mar 2024 11:24:52 +0200 Subject: [PATCH 2/4] chore: args format fix for near `4.0.10` (#1813) --- docs/3.tutorials/examples/donation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/3.tutorials/examples/donation.md b/docs/3.tutorials/examples/donation.md index 379f4606567..27edf7203e1 100644 --- a/docs/3.tutorials/examples/donation.md +++ b/docs/3.tutorials/examples/donation.md @@ -211,7 +211,7 @@ To interact with the contract through the console, you can use the following com ```bash # Get donations # Optional arguments for pagination -near view donation.near-examples.testnet get_donations --args='{"from_index": "0","limit": "10"}' +near view donation.near-examples.testnet get_donations --args='{"from_index": 0,"limit": 10}' # Get beneficiary near view donation.near-examples.testnet get_beneficiary From bab41d4b90aa11be0bfc997170cb80dd26499aeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dami=C3=A1n=20Parrino?= Date: Wed, 13 Mar 2024 06:25:45 -0300 Subject: [PATCH 3/4] add meta transactions (#1814) --- docs/1.concepts/abstraction/meta-tx.md | 168 ++++++++++++++++++++++++ docs/1.concepts/abstraction/relayers.md | 5 +- website/sidebars.js | 1 + 3 files changed, 172 insertions(+), 2 deletions(-) create mode 100644 docs/1.concepts/abstraction/meta-tx.md diff --git a/docs/1.concepts/abstraction/meta-tx.md b/docs/1.concepts/abstraction/meta-tx.md new file mode 100644 index 00000000000..e3b8c435228 --- /dev/null +++ b/docs/1.concepts/abstraction/meta-tx.md @@ -0,0 +1,168 @@ +--- +id: meta-transactions +title: Meta Transactions +--- + +# Meta Transactions + +[NEP-366](https://github.com/near/NEPs/pull/366) introduced the concept of meta +transactions to Near Protocol. This feature allows users to execute transactions +on NEAR without owning any gas or tokens. In order to enable this, users +construct and sign transactions off-chain. A third party (the relayer) is used +to cover the fees of submitting and executing the transaction. + +## Overview + +![Flow chart of meta +transactions](https://raw.githubusercontent.com/near/NEPs/003e589e6aba24fc70dd91c9cf7ef0007ca50735/neps/assets/nep-0366/NEP-DelegateAction.png) +_Credits for the diagram go to the NEP authors Alexander Fadeev and Egor +Uleyskiy._ + + +The graphic shows an example use case for meta transactions. Alice owns an +amount of the fungible token `$FT`. She wants to transfer some to John. To do +that, she needs to call `ft_transfer("john", 10)` on an account named `FT`. + +The problem is, Alice has no NEAR tokens. She only has a NEAR account that +someone else funded for her and she owns the private keys. She could create a +signed transaction that would make the `ft_transfer("john", 10)` call. But +validator nodes will not accept it, because she does not have the necessary Near +token balance to purchase the gas. + +With meta transactions, Alice can create a `DelegateAction`, which is very +similar to a transaction. It also contains a list of actions to execute and a +single receiver for those actions. She signs the `DelegateAction` and forwards +it (off-chain) to a relayer. The relayer wraps it in a transaction, of which the +relayer is the signer and therefore pays the gas costs. If the inner actions +have an attached token balance, this is also paid for by the relayer. + +On chain, the `SignedDelegateAction` inside the transaction is converted to an +action receipt with the same `SignedDelegateAction` on the relayer's shard. The +receipt is forwarded to the account from `Alice`, which will unpacked the +`SignedDelegateAction` and verify that it is signed by Alice with a valid Nonce, +etc. If all checks are successful, a new action receipt with the inner actions +as body is sent to `FT`. There, the `ft_transfer` call finally executes. + +## Relayer + +Meta transactions only work with a [relayer](relayers.md). This is an application layer +concept, implemented off-chain. Think of it as a server that accepts a +`SignedDelegateAction`, does some checks on them and eventually forwards it +inside a transaction to the blockchain network. + +A relayer may choose to offer their service for free but that's not going to be +financially viable long-term. But they could easily have the user pay using +other means, outside of Near blockchain. And with some tricks, it can even be +paid using fungible tokens on Near. + +In the example visualized above, the payment is done using $FT. Together with +the transfer to John, Alice also adds an action to pay 0.1 $FT to the relayer. +The relayer checks the content of the `SignedDelegateAction` and only processes +it if this payment is included as the first action. In this way, the relayer +will be paid in the same transaction as John. + +:::warning Keep in mind +The payment to the relayer is still not guaranteed. It could be that +Alice does not have sufficient `$FT` and the transfer fails. To mitigate, the +relayer should check the `$FT` balance of Alice first. +::: + +Unfortunately, this still does not guarantee that the balance will be high +enough once the meta transaction executes. The relayer could waste NEAR gas +without compensation if Alice somehow reduces her $FT balance in just the right +moment. Some level of trust between the relayer and its user is therefore +required. + +## Limitations + +### Single receiver + +A meta transaction, like a normal transaction, can only have one receiver. It's +possible to chain additional receipts afterwards. But crucially, there is no +atomicity guarantee and no roll-back mechanism. + +### Accounts must be initialized + +Any transaction, including meta transactions, must use NONCEs to avoid replay +attacks. The NONCE must be chosen by Alice and compared to a NONCE stored on +chain. This NONCE is stored on the access key information that gets initialized +when creating an account. + +## Constraints on the actions inside a meta transaction + +A transaction is only allowed to contain one single delegate action. Nested +delegate actions are disallowed and so are delegate actions next to each other +in the same receipt. + +## Gas costs for meta transactions + +Meta transactions challenge the traditional ways of charging gas for actions. +Let's assume Alice uses a relayer to +execute actions with Bob as the receiver. + +1. The relayer purchases the gas for all inner actions, plus the gas for the + delegate action wrapping them. +2. The cost of sending the inner actions and the delegate action from the + relayer to Alice's shard will be burned immediately. The condition `relayer + == Alice` determines which action `SEND` cost is taken (`sir` or `not_sir`). + Let's call this `SEND(1)`. +3. On Alice's shard, the delegate action is executed, thus the `EXEC` gas cost + for it is burned. Alice sends the inner actions to Bob's shard. Therefore, we + burn the `SEND` fee again. This time based on `Alice == Bob` to figure out + `sir` or `not_sir`. Let's call this `SEND(2)`. +4. On Bob's shard, we execute all inner actions and burn their `EXEC` cost. + +Each of these steps should make sense and not be too surprising. But the +consequence is that the implicit costs paid at the relayer's shard are +`SEND(1)` + `SEND(2)` + `EXEC` for all inner actions plus `SEND(1)` + `EXEC` for +the delegate action. This might be surprising but hopefully with this +explanation it makes sense now! + +## Gas refunds in meta transactions + +Gas refund receipts work exactly like for normal transaction. At every step, the +difference between the pessimistic gas price and the actual gas price at that +height is computed and refunded. At the end of the last step, additionally all +remaining gas is also refunded at the original purchasing price. The gas refunds +go to the signer of the original transaction, in this case the relayer. This is +only fair, since the relayer also paid for it. + +## Balance refunds in meta transactions + +Unlike gas refunds, the protocol sends balance refunds to the predecessor +(a.k.a. sender) of the receipt. This makes sense, as we deposit the attached +balance to the receiver, who has to explicitly reattach a new balance to new +receipts they might spawn. + +In the world of meta transactions, this assumption is also challenged. If an +inner action requires an attached balance (for example a transfer action) then +this balance is taken from the relayer. + +The relayer can see what the cost will be before submitting the meta transaction +and agrees to pay for it, so nothing wrong so far. But what if the transaction +fails execution on Bob's shard? At this point, the predecessor is `Alice` and +therefore she receives the token balance refunded, not the relayer. This is +something relayer implementations must be aware of since there is a financial +incentive for Alice to submit meta transactions that have high balances attached +but will fail on Bob's shard. + +## Function access keys in meta transactions + +Function access keys can limit the allowance, the receiving contract, and the +contract methods. The allowance limitation acts slightly strange with meta +transactions. + +But first, both the methods and the receiver will be checked as expected. That +is, when the delegate action is unwrapped on Alice's shard, the access key is +loaded from the DB and compared to the function call. If the receiver or method +is not allowed, the function call action fails. + +For allowance, however, there is no check. All costs have been covered by the +relayer. Hence, even if the allowance of the key is insufficient to make the call +directly, indirectly through meta transaction it will still work. + +This behavior is in the spirit of allowance limiting how much financial +resources the user can use from a given account. But if someone were to limit a +function access key to one trivial action by setting a very small allowance, +that is circumventable by going through a relayer. An interesting twist that +comes with the addition of meta transactions. diff --git a/docs/1.concepts/abstraction/relayers.md b/docs/1.concepts/abstraction/relayers.md index e450b2d4704..2ac423a6695 100644 --- a/docs/1.concepts/abstraction/relayers.md +++ b/docs/1.concepts/abstraction/relayers.md @@ -9,9 +9,9 @@ A relayer is a simple web service that receives transactions from NEAR users, an ## How it works -Relayers are a natural consequence of [NEP-366: Meta Transactions](https://github.com/near/NEPs/blob/master/neps/nep-0366.md), a special type of transaction which can be best understood as an intent. +Relayers are a natural consequence of [Meta Transactions](meta-tx.md) ([NEP-366](https://github.com/near/NEPs/blob/master/neps/nep-0366.md)), a special type of transaction which can be best understood as an intent. -The user expresses: "I want to do a specific action on chain" and signs this intent **off-chain**, but does not sends it to the network. Instead, they send the intent to a `Relayer`, which wraps the message into an actual transaction, attaches the necessary funds, and sends it to the network. +The user expresses: _"I want to do a specific action on chain"_ and signs this intent **off-chain**, but does not sends it to the network. Instead, they send the intent to a `Relayer`, which wraps the message into an actual transaction, attaches the necessary funds, and sends it to the network.
Technical Details @@ -26,6 +26,7 @@ The `SignedTransaction` is then sent to the network via RPC call, and the result --- ## Why using a Relayer? + There are multiple reasons to use a relayer: 1. Your users are new to NEAR and don't have any gas to cover transactions 2. Your users have an account on NEAR, but only have a Fungible Token Balance. They can now use the FT to pay for gas diff --git a/website/sidebars.js b/website/sidebars.js index 8317f8cc73f..417b3e9265a 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -67,6 +67,7 @@ const sidebar = { "value": " Chain Abstraction ✨" }, "concepts/abstraction/introduction", + "concepts/abstraction/meta-transactions", "concepts/abstraction/relayers", { "Chain Signatures": [ From fcb2f1138329774383070ceb00d3289e69ac779f Mon Sep 17 00:00:00 2001 From: Olga Telezhnaya Date: Wed, 13 Mar 2024 19:04:14 +0000 Subject: [PATCH 4/4] rpc: update transactions page (#1816) * rpc: update transactions page * Apply suggestions from code review --- docs/5.api/rpc/transactions.md | 125 ++++++++++++++++++--------------- 1 file changed, 70 insertions(+), 55 deletions(-) diff --git a/docs/5.api/rpc/transactions.md b/docs/5.api/rpc/transactions.md index 953cfdb0cf7..980f408c2bd 100644 --- a/docs/5.api/rpc/transactions.md +++ b/docs/5.api/rpc/transactions.md @@ -18,31 +18,11 @@ The RPC API enables you to send transactions and query their status. - method: `send_tx` - params: - - SignedTransaction encoded in base64 - - [Optional] `wait_until`: the required minimal execution level. It can be one of the listed below. The default value is `FINAL`. + - `signed_tx_base64`: SignedTransaction encoded in base64 + - [Optional] `wait_until`: the required minimal execution level. [Read more here](#tx-status-result). The default value is `EXECUTED_OPTIMISTIC`. -```rust -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum TxExecutionStatus { - /// Transaction is waiting to be included into the block - None, - /// Transaction is included into the block. The block may be not finalized yet - Included, - /// Transaction is included into finalized block - IncludedFinal, - /// Transaction is included into finalized block + - /// All the transaction receipts finished their execution. - /// The corresponding blocks for each receipt may be not finalized yet - Executed, - /// Transaction is included into finalized block + - /// Execution of transaction receipts is finalized - #[default] - Final, -} -``` - -Using `send_tx` with finality `NONE` is equal to legacy `broadcast_tx_async` method. -Using `send_tx` with finality `FINAL` is equal to legacy `broadcast_tx_commit` method. +Using `send_tx` with `wait_until = NONE` is equal to legacy `broadcast_tx_async` method. +Using `send_tx` with finality `wait_until = EXECUTED_OPTIMISTIC` is equal to legacy `broadcast_tx_commit` method. Example: @@ -260,8 +240,13 @@ Here is the exhaustive list of the error variants that can be returned by `broad - method: `tx` - params: - - `transaction hash` _(see [NearBlocks Explorer](https://testnet.nearblocks.io) for a valid transaction hash)_ - - `sender account id` + - `tx_hash` _(see [NearBlocks Explorer](https://testnet.nearblocks.io) for a valid transaction hash)_ + - `sender_account_id` _(used to determine which shard to query for transaction)_ + - [Optional] `wait_until`: the required minimal execution level. Read more [here](/api/rpc/transactions#tx-status-result). The default value is `EXECUTED_OPTIMISTIC`. + +A Transaction status request with `wait_until != NONE` will wait until the transaction appears on the blockchain. +If the transaction does not exist, the method will wait until the timeout is reached. +If you only need to check whether the transaction exists, use `wait_until = NONE`, it will return the response immediately. Example: @@ -273,26 +258,20 @@ Example: "jsonrpc": "2.0", "id": "dontcare", "method": "tx", - "params": ["6zgh2u9DqHHiXzdy9ouTP7oGky2T4nugqzqt9wJZwNFm", "sender.testnet"] + "params": { + "tx_hash": "6zgh2u9DqHHiXzdy9ouTP7oGky2T4nugqzqt9wJZwNFm", + "sender_account_id": "sender.testnet", + "wait_until": "EXECUTED" + } } ``` - - - -```js -const response = await near.connection.provider.txStatus( - "6zgh2u9DqHHiXzdy9ouTP7oGky2T4nugqzqt9wJZwNFm", - "sender.testnet" -); -``` - ```bash http post https://rpc.testnet.near.org jsonrpc=2.0 id=dontcare method=tx \ - params:='[ "6zgh2u9DqHHiXzdy9ouTP7oGky2T4nugqzqt9wJZwNFm", "sender.testnet"]' + params:='{"tx_hash": "6zgh2u9DqHHiXzdy9ouTP7oGky2T4nugqzqt9wJZwNFm", "sender_account_id": "sender.testnet"}' ``` @@ -306,6 +285,7 @@ http post https://rpc.testnet.near.org jsonrpc=2.0 id=dontcare method=tx \ { "jsonrpc": "2.0", "result": { + "final_execution_status": "FINAL", "status": { "SuccessValue": "" }, @@ -504,8 +484,14 @@ Here is the exhaustive list of the error variants that can be returned by `tx` m - method: `EXPERIMENTAL_tx_status` - params: - - `transaction hash` _(see [NearBlocks Explorer](https://testnet.nearblocks.io) for a valid transaction hash)_ - - `sender account id` _(used to determine which shard to query for transaction)_ + - `tx_hash` _(see [NearBlocks Explorer](https://testnet.nearblocks.io) for a valid transaction hash)_ + - `sender_account_id` _(used to determine which shard to query for transaction)_ + - [Optional] `wait_until`: the required minimal execution level. Read more [here](/api/rpc/transactions#tx-status-result). The default value is `EXECUTED_OPTIMISTIC`. + +A Transaction status request with `wait_until != NONE` will wait until the transaction appears on the blockchain. +If the transaction does not exist, the method will wait until the timeout is reached. +If you only need to check whether the transaction exists, use `wait_until = NONE`, it will return the response immediately. + Example: @@ -517,25 +503,19 @@ Example: "jsonrpc": "2.0", "id": "dontcare", "method": "EXPERIMENTAL_tx_status", - "params": ["HEgnVQZfs9uJzrqTob4g2Xmebqodq9waZvApSkrbcAhd", "bowen"] + "params": { + "tx_hash": "HEgnVQZfs9uJzrqTob4g2Xmebqodq9waZvApSkrbcAhd", + "sender_account_id": "bowen", + "wait_until": "EXECUTED" + } } ``` - - - -```js -const response = await near.connection.provider.experimental_txStatus( - "HEgnVQZfs9uJzrqTob4g2Xmebqodq9waZvApSkrbcAhd", - "bowen" -); -``` - ```bash -http post https://rpc.testnet.near.org jsonrpc=2.0 method=EXPERIMENTAL_tx_status params:='["HEgnVQZfs9uJzrqTob4g2Xmebqodq9waZvApSkrbcAhd", "bowen"]' id=dontcare +http post https://rpc.testnet.near.org jsonrpc=2.0 method=EXPERIMENTAL_tx_status params:='{"tx_hash": "HEgnVQZfs9uJzrqTob4g2Xmebqodq9waZvApSkrbcAhd", "sender_account_id": "bowen"}' id=dontcare ``` @@ -550,6 +530,7 @@ http post https://rpc.testnet.near.org jsonrpc=2.0 method=EXPERIMENTAL_tx_status "id": "123", "jsonrpc": "2.0", "result": { + "final_execution_status": "FINAL", "receipts": [ { "predecessor_id": "bowen", @@ -1020,14 +1001,45 @@ Here is the exhaustive list of the error variants that can be returned by `EXPER +## Transaction Execution Levels {#tx-status-result} + +All the methods listed above have `wait_until` request parameter, and `final_execution_status` response value. +They correspond to the same enum `TxExecutionStatus`. +See the detailed explanation for all the options: + +```rust +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum TxExecutionStatus { + /// Transaction is waiting to be included into the block + None, + /// Transaction is included into the block. The block may be not finalized yet + Included, + /// Transaction is included into the block + + /// All the transaction receipts finished their execution. + /// The corresponding blocks for tx and each receipt may be not finalized yet + #[default] + ExecutedOptimistic, + /// Transaction is included into finalized block + IncludedFinal, + /// Transaction is included into finalized block + + /// All the transaction receipts finished their execution. + /// The corresponding blocks for each receipt may be not finalized yet + Executed, + /// Transaction is included into finalized block + + /// Execution of transaction receipts is finalized + Final, +} +``` + --- # Deprecated methods {#deprecated} -## Send transaction (async) {#send-transaction-async} +## [deprecated] Send transaction (async) {#send-transaction-async} + +> Consider using [`send_tx`](/api/rpc/transactions#send-tx) instead > Sends a transaction and immediately returns transaction hash. -> Consider using `send_tx` - method: `broadcast_tx_async` - params: [SignedTransaction encoded in base64] @@ -1133,7 +1145,9 @@ Here is the exhaustive list of the error variants that can be returned by `broad --- -## Send transaction (await) {#send-transaction-await} +## [deprecated] Send transaction (await) {#send-transaction-await} + +> Consider using [`send_tx`](/api/rpc/transactions#send-tx) instead > Sends a transaction and waits until transaction is fully complete. _(Has a 10 second timeout)_ @@ -1177,6 +1191,7 @@ http post https://rpc.testnet.near.org jsonrpc=2.0 id=dontcare method=broadcast_ { "jsonrpc": "2.0", "result": { + "final_execution_status": "FINAL", "status": { "SuccessValue": "" },