承接上一篇构建Near Dapp Orderly 流程我们知道了去生成OrderlyKey和TradingKey进入OrderlyNetwork,现在来看看如何来充值、提现以及如何查询余额。
充值 - Deposit
我们的near token在我们的钱包里面,要想在DeFi中交易,需要将token充到合约里面。
先创建一个deposit.component.tsx
文件,这个组件中可以输入金额,然后点击按钮就可以deposit Near:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import {ChangeEvent, useState} from "react";
export function DepositComponent() { const [amount, setAmount] = useState<string>('1');
const onDepositClick = () => { console.log('value', amount); }
const changeValue = (e: ChangeEvent) => { setAmount((e.target as HTMLInputElement).value); } return ( <form className='flex justify-center gap-2 '> <input type='number' value={amount} onChange={changeValue}/> <div onClick={onDepositClick}>Deposit</div> </form> ) }
|
在充值之前,我们需要获得一个storage_cost_of_token_balance
数值:
1 2 3 4 5 6 7
| export const storageCostOfTokenBalance = () => callViewFunction({ contractName: environment.nearWalletConfig.contractName, methodName: 'storage_cost_of_token_balance', args: {}, });
|
然后判断是否需要storage_deposit
,然后再depsoit:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| export const depositNear = async (wallet: WalletConnection, amount: string) => { const accountId = wallet.getAccountId(); const keyPair = await environment.nearWalletConfig.keyStore.getKey(environment.nearWalletConfig.networkId, accountId); const publicKey = keyPair.getPublicKey();
const [storageUsage, balanceOf, storageCost, accessKeyInfo] = await Promise.all([ userStorageUsage(accountId), storageBalanceOf(environment.nearWalletConfig.contractName, accountId), storageCostOfTokenBalance(), getAccessKeyInfo(accountId, keyPair) ]); const value = new BigNumber(storageUsage).plus(new BigNumber(storageCost)).minus(new BigNumber(balanceOf.total));
const nonce = ++accessKeyInfo.nonce; const recentBlockHash = serialize.base_decode(accessKeyInfo.block_hash); const transactions: Transaction[] = [];
if (value.isGreaterThan(0)) { transactions.push(createTransaction( accountId, publicKey, environment.nearWalletConfig.contractName, nonce, [ functionCall( 'storage_deposit', { receiver_id: environment.nearWalletConfig.contractName, msg: '',
}, BOATLOAD_OF_GAS, value.toFixed(), ) ], recentBlockHash, )) } transactions.push(createTransaction( accountId, publicKey, environment.nearWalletConfig.contractName, nonce + 1, [ functionCall( 'user_deposit_native_token', {}, BOATLOAD_OF_GAS, utils.format.parseNearAmount(amount), ) ], recentBlockHash, )) return wallet.requestSignTransactions({ transactions, })
|
然后在deposit.component.tsx
中调用这个方法:
1 2 3 4 5 6 7 8 9 10
| import {ChangeEvent, useState} from "react"; import {useConnection} from "./ConnectionContext"; import {depositNear} from "./services/contract.service";
export function DepositComponent() { const onDepositClick = () => { depositNear(walletConnection!, amount).then(); } }
|
当点击deposit
按钮后,会跳转到MyNearWallet去签名,在签名页面我们可以观察到,如果有需要storage_deposit
操作的话,会两个transaction一起签名的。
在签名完毕后,可以在explorer里面查看对应的签名信息,确认是成功了的。
通过REST API 查询Balance
REST Api 我们需要增加一些配置在environment里面:
1 2 3 4 5 6 7 8
| import {keyStores} from "near-api-js";
export const environment = { config: { apiUrl: 'https://testnet-api.orderly.org', } }
|
然后通过/position/balances
这个接口可以查询balance。但是调用这个接口需要用OrderlyKey签名。
首先看签名函数:
1 2 3 4 5
| export const signMessageByOrderlyKey = (params: string, keyPair: KeyPair) => { const u8 = Buffer.from(params); const signStr = keyPair.sign(u8); return base64url(Buffer.from(signStr.signature).toString('base64')); }
|
然后我们创建asset.service.ts
来写这个api请求的调用方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| export const getBalance = async (): Promise<any> => { const urlParam = '/position/balances'; const accountId = 'neardapp-t1.testnet'; const timestamp = new Date().getTime().toString(); const messageStr = [timestamp, 'GET', urlParam].join(''); const keyPair = await environment.nearWalletConfig.keyStore.getKey(environment.nearWalletConfig.networkId, accountId) const sign = signMessageByOrderlyKey(messageStr, keyPair);
const headers = { 'Access-Control-Allow-Origin': '*', Accept: 'application/json', 'Content-Type': 'application/json;charset=utf-8', } Object.assign(headers, { 'orderly-account-id': accountId, 'orderly-key': keyPair?.getPublicKey().toString(), 'orderly-timestamp': timestamp, 'orderly-signature': sign, }); return fetch(environment.config.apiUrl + '/position/balances', { method: 'GET', headers, }) .then(response => response.json()); }
|
调用这个方法,可以观察到我们现在的balances里面有个near,holding数值是我们前面deposit的数值。
提现 - Withdraw
充值是将钱包里面的钱放进合约进行交易。
提现是将合约账号里面的钱取出放到钱包。
同样,我们创建一个withdraw.component.tsx
组件,这个组件中我们可以输入需要提现的金额,然后有个withdraw按钮:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| import {useConnection} from "./ConnectionContext"; import {ChangeEvent, useEffect, useState} from "react"; import {withdrawNear} from "./services/contract.service"; import {getTokenList} from "./services/asset.service";
interface TokenConfigInterface{ token: string; token_account_id: string; }
export function WithdrawComponent() { const [amount, setAmount] = useState<string>(''); const onWithdraw = () => {
} const onChange = (e: ChangeEvent) => { setAmount((e.target as HTMLInputElement).value); } return ( <form className='flex justify-center gap-2 mt-2'> <input type='number' value={amount} onChange={onChange}/> <div onClick={onWithdraw}>Withdraw</div> </form> ) }
|
提现基本和充值差不多,有个不同的地方是需要获取一下withdrawFee:
1 2 3 4 5 6
| export const getWithdrawFee = async () => callViewFunction({ contractName: environment.nearWalletConfig.contractName, methodName: 'get_withdraw_fee', args: {}, });
|
并且需要知道near的tokenAddress,当然这里我们只针对near原生token,不涉及到非原生代币,所以我们知道Near的tokenAddress是near
。
然后看withdraw方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| export const withdrawNear = async (wallet: WalletConnection, tokenAddress: string, amount: string) => { const accountId = wallet.getAccountId(); const keyPair = await environment.nearWalletConfig.keyStore.getKey(environment.nearWalletConfig.networkId, accountId); const publicKey = keyPair.getPublicKey();
const [storageUsage, balanceOf, storageCost, withdrawFee, accessKeyInfo] = await Promise.all([ userStorageUsage(accountId), storageBalanceOf(environment.nearWalletConfig.contractName, accountId), storageCostOfTokenBalance(), getWithdrawFee(), getAccessKeyInfo(accountId, keyPair) ]); const recentBlockHash = serialize.base_decode(accessKeyInfo.block_hash); const value = new BigNumber(storageUsage).plus(new BigNumber(storageCost)).minus(new BigNumber(balanceOf.total)); const transactions: Transaction[] = []; if (value.isGreaterThan(0)) { transactions.push(createTransaction( accountId, publicKey, environment.nearWalletConfig.contractName, accessKeyInfo.nonce + 1, [ functionCall( 'storage_deposit', { receiver_id: environment.nearWalletConfig.contractName, msg: '',
}, BOATLOAD_OF_GAS, value.toFixed(), ) ], recentBlockHash, )) }
transactions.push(createTransaction( accountId, publicKey, environment.nearWalletConfig.contractName, accessKeyInfo.nonce + 2, [ functionCall( 'user_request_withdraw', { token: tokenAddress, amount: utils.format.parseNearAmount(amount), }, BOATLOAD_OF_GAS, withdrawFee, ) ], recentBlockHash, ))
return wallet.requestSignTransactions({ transactions, }); }
|
然后在withdraw.component.tsx
中完善调用:
1 2 3 4 5 6 7 8
| const onWithdraw = () => { const tokenData = tokenList.find(item => item.token === 'NEAR'); console.log('token data', tokenData); if (!tokenData) { return false; } withdrawNear(walletConnection!, 'near', amount).then(); }
|
当点击withdraw按钮的时候,会跳转到MyNearWallet进行签名,在签名页面,如果有需要storage_depsoit
的话可以看到有两个transaction。签名完成后,我们可以再请求balance接口看看数据。
但是这个数据不是实时的,可能会有点延迟。
比如我们前面deposit了1near,现在withdraw了1near,那么这个balance里面near的holding就是0.
充提是调用合约方法,麻烦的是需要在调用之前去判断一些费用是否足够,否则合约操作会失败。
这里也顺带看了在调用REST API的时候需要先签名,如果涉及到用户数据的接口,需要判断签名的。