How would one go about building an escrow and multisig contract on NEAR?
E.g. in comparison to building the contract in Solidity; what would be needed, are there any examples?
I can't compare this for solidity. But on NEAR multisig contract can be implemented either using multiple AccessKeys or using multiple predecessors.
The following steps are needed:
Propose an action e.g. transfer X to Y to the contract by calling propose() on the contract. This should return a proposal_id
Aggregate approvals for the proposal_id using approve() or reject() calls.
Once necessary amount of approvals are collected. A caller can call issue and pass the proposal_id, it will issue the action and close the proposal.
Seems there are two questions here, for escrow and multisig. Evgeny has taken on the multisig part.
We do have a simple example of escrow on NEAR written in Rust.
This example has no frontend but provides the lower-level CLI commands needed to show an escrow in use.
https://examples.nearprotocol.com/rust-fungible-token
There is an example of multisig contract now here written in Rust - https://github.com/near/core-contracts/tree/master/multisig
The main benefit and why it becomes way simpler, is that NEAR Accounts already maintain set of keys per account. Contract just needs to manage the requests and execute them when enough confirmation have been received.
Related
How is that possible after revoking access keys to a contract? is there a standard design pattern to enable that?
I am not aware of any "standard design pattern" here. One way to do this is to have some method that only some permissioned account(s) can call to retrieve tokens from the smart contract. You can implement any logic you like in that method, including how much NEAR can be withdrawn each time.
More concretely, it looks like
pub fn claim_owner_rebate(amount: U128) -> Promise {
assert_eq!(env::predecessor_id(), self.owner_id);
// do some stuff to check whether this amount can be withdrawn
// ...
Promise::new(env::predecessor_id()).transfer(amount)
}
Let's say I have an API Gateway for third parties to create orders in my system. As a part of order creation I need to validate that the request model I have been provided is correct - not just statically but by checking foreign keys are valid - that the product id’s are valid in the order, the account id is valid. If not I want to return a 400 to let the caller know they have passed an erroneous request.
What I would expect to do is to create an orders::createOrder lambda function, which would make parallel calls to products::listProducts, accounts::listAccountsForCustomer and other microservices to retrieve the information needed for validation, before I am happy to create the order in the system. This validation needs to happen synchronously as it’s a request/response from a third party to create the order.
I would usually want the logical domains - customers, products, orders, accounts to be separate microservices, and I usually have some logic in an API Gateway layer for orchestration / mapping to the microservices below. I've been reading that calling Lambda from Lambda is a bad idea..
How do I correctly model this on serverless?
For your case it'll be best to keep all this logic within one lambda. Splitting it into multiple smaller functions will add latency, so you have worse user experience and you'll multiply your cost since you have multiple functions running. You can also try Step Functions if you want such split. But it's also pretty expensive, I don't recommend it for such simple case.
In Ethereum Events are clearly defined, each one is a data structure used to signal some action. In Near, env::log are messages.
Example:
In Ethereum we use logs for most of the token state changes
NEP-21 dosn't say anything about logs.
Is there any point of using logs in near, except "debug" / return user information? Should logs be standarized ?
Maybe it's better to have this discussion in some other place...?
Following on that:
Transaction arguments are serialized and clearly visible. Is there any point to log transaction arguments?
For example: in Ethereum token transfer functions, all arguments are recorded additionally in an event. There are few reasons for that:
With events we should be able to recreate a state of the contract;
it's more readable while browsing a blockchain.
But in case of transfer, I don't think there is any added value, because we don't log anything else than the function arguments.
We haven't added analog of Ethereum events into NEAR yet. Contracts that want to signal about some event need to return it as part of the method result like here. Therefore our env::log is currently for informational purposes only and can be used for debugging and such.
In general, our contracts can choose how to serialize arguments, so it might be useful for the contract to log its own arguments since it might be using a complex serialization mechanism for them.
It might be useful to have logs for complex contracts with complex cross contract calls and callbacks so that it can inform the user how it arrived to a specific result.
Im trying to create a function or an extrinsic that doesn't have a transaction fee for the origin, but rather totally free. I thought maby with a weight of 0 it would be solved but it still costs tokens,
#[weight = 0]
then i tried to adjust the state with an rpc call, which did some calculations but did not modify the state
How can i create a function/extrinsic that is free without any transaction fee? And is it possible for rpc calls to adjust the state?
This is actually very easy with Substrate.
You simply pass Pays::No to the weight of the function.
Like so:
#[weight = (100_000, DispatchClass::Normal, Pays::No)]
Here the tuple describes:
The weight of the function. You should put a real value here to represent how complex this function is for your blockhain's computation.
The DispatchClass of this function. The default choice is Normal
The Pays option which determines if the caller will pay a fee or not.
Note that if you create an extrinsic that a user does not pay any fees, your blockchain is immediately vulnerable to DDOS attacks, as any user could spam this function at no cost.
You will need to build other layers of verification at your blockchain to make sure only valid calls to this function are propagated to other nodes.
Take a look here:
https://github.com/paritytech/polkadot/blob/master/runtime/common/src/claims.rs#L386
In this case, we have some statement which we verify is correctly signed by the user making the call before the call is passed to other nodes:
https://github.com/paritytech/polkadot/blob/master/runtime/common/src/claims.rs#L592
So you must do the same if you want your blockchain to be safe with a free function like this.
Let's say I have a contract function that expects a certain amount of near to be send with a certain transaction, the function is called create_order, create_order takes a couple arguments.
I have my contract setup in the frontend under the name myContract.
I want to call myContract.create_order({...}) but the transaction fails because this method call doesn't have the right amount of NEAR tokens attached.
How do I assign a certain value of deposit to a transaction?
It's possible to use account.functionCall directly (without sugar for RPCs) to either attach amount or specify gas allowance for the call.
See Account#functionCall in nearlib.
Nearlib supports it using account.functionCall(..., amount). But it might not work, because of the design of the access keys with function calls. Default authorized access keys towards applications only allows function calls without attached token deposits (only prepaid gas). It's done this way to prevent apps from automatically using your balance without your explicit approval. Details on access keys are here: https://github.com/nearprotocol/NEPs/blob/master/text/0005-access-keys.md
The way to attach a deposit for the transaction should be done with the explicit approval from the wallet. The app should create a request for the wallet, redirect to the wallet for the approval (or through the popup). Once user approves the transaction, it's signed with full access key from the wallet directly and broadcasted. But I'm afraid we don't have this API on the wallet yet. Issue for this: https://github.com/nearprotocol/near-wallet/issues/56
AFAIK it is not supported at the moment. It will be available after this NEP https://github.com/nearprotocol/NEPs/pull/13 lands.