From 9325ac10f63a168fbdec0b046364aac54efc62cf Mon Sep 17 00:00:00 2001 From: Matt Lockyer Date: Tue, 29 Sep 2020 07:46:58 -0700 Subject: [PATCH 1/3] more node aware code and added node-fetch for CH methods --- dist/near-api-js.js | 44 +++++++++++++++++++++++------------------ dist/near-api-js.min.js | 6 +++--- lib/account_multisig.js | 19 +++++++++++++++--- package.json | 2 +- src/account.ts | 1 + src/account_multisig.ts | 23 ++++++++++++++++++--- yarn.lock | 2 +- 7 files changed, 67 insertions(+), 30 deletions(-) diff --git a/dist/near-api-js.js b/dist/near-api-js.js index fd84857f0a..51bdc3d950 100644 --- a/dist/near-api-js.js +++ b/dist/near-api-js.js @@ -414,12 +414,25 @@ exports.MULTISIG_DEPOSIT = new bn_js_1.default('0'); exports.MULTISIG_CHANGE_METHODS = ['add_request', 'add_request_and_confirm', 'delete_request', 'confirm']; exports.MULTISIG_VIEW_METHODS = ['get_request_nonce', 'list_request_ids']; exports.MULTISIG_CONFIRM_METHODS = ['confirm']; +const IS_BROWSER = typeof window !== 'undefined'; ; +// in memory request cache for node w/o localStorage +let __multisigRequest = null; class AccountMultisig extends account_1.Account { constructor(connection, accountId) { super(connection, accountId); this.contract = getContract(this); } + // overrides + // async findAccessKey(receiverId, actions) { + // const accessKeys = await this.getAccessKeys() + // const accessKey = accessKeys.find((k) => + // k.access_key.permission && + // k.access_key.permission.FunctionCall && + // k.access_key.permission.FunctionCall.method_names.some((mn) => MULTISIG_CHANGE_METHODS.includes(mn)) + // ); + // return { publicKey: accessKey.public_key, accessKey: accessKey.access_key} + // } async addKey(publicKey, contractId, methodName, amount) { if (contractId) { return super.addKey(publicKey, contractId, exports.MULTISIG_CHANGE_METHODS.join(), exports.MULTISIG_ALLOWANCE); @@ -492,10 +505,16 @@ class AccountMultisig extends account_1.Account { return actions && actions[0] && actions[0].functionCall && actions[0].functionCall.methodName === 'delete_request'; } getRequest() { - return JSON.parse(localStorage.getItem(`__multisigRequest`) || `{}`); + if (IS_BROWSER) { + return JSON.parse(localStorage.getItem(`__multisigRequest`) || `{}`); + } + return __multisigRequest; } setRequest(data) { - localStorage.setItem(`__multisigRequest`, JSON.stringify(data)); + if (IS_BROWSER) { + return localStorage.setItem(`__multisigRequest`, JSON.stringify(data)); + } + __multisigRequest = data; } // default helpers for CH async sendRequestCode() { @@ -588,7 +607,7 @@ const convertActions = (actions, accountId, receiverId) => actions.map((a) => { args: (args && Buffer.from(args).toString('base64')) || undefined, code: (code && Buffer.from(code).toString('base64')) || undefined, amount: (deposit && deposit.toString()) || undefined, - deposit: (deposit && deposit.toString()) || undefined, + deposit: (deposit && deposit.toString()) || '0', permission: undefined, }; if (accessKey) { @@ -2370,7 +2389,7 @@ class JsonRpcProvider extends provider_1.Provider { */ async sendTransaction(signedTransaction) { const bytes = signedTransaction.encode(); - return this.sendJsonRpc('broadcast_tx_commit', [Buffer.from(bytes).toString('base64')]).then(provider_1.adaptTransactionResult); + return this.sendJsonRpc('broadcast_tx_commit', [Buffer.from(bytes).toString('base64')]); } /** * Gets a transaction's status from the RPC @@ -2380,7 +2399,7 @@ class JsonRpcProvider extends provider_1.Provider { * @returns {Promise} */ async txStatus(txHash, accountId) { - return this.sendJsonRpc('tx', [serialize_1.base_encode(txHash), accountId]).then(provider_1.adaptTransactionResult); + return this.sendJsonRpc('tx', [serialize_1.base_encode(txHash), accountId]); } /** * Query the RPC as [shown in the docs](https://docs.nearprotocol.com/docs/interaction/rpc#query) @@ -2494,7 +2513,7 @@ exports.JsonRpcProvider = JsonRpcProvider; (function (Buffer){ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.adaptTransactionResult = exports.getTransactionLastResult = exports.Provider = exports.IdType = exports.FinalExecutionStatusBasic = exports.ExecutionStatusBasic = void 0; +exports.getTransactionLastResult = exports.Provider = exports.IdType = exports.FinalExecutionStatusBasic = exports.ExecutionStatusBasic = void 0; var ExecutionStatusBasic; (function (ExecutionStatusBasic) { ExecutionStatusBasic["Unknown"] = "Unknown"; @@ -2528,19 +2547,6 @@ function getTransactionLastResult(txResult) { return null; } exports.getTransactionLastResult = getTransactionLastResult; -function adaptTransactionResult(txResult) { - if ('receipts' in txResult) { - txResult = { - status: txResult.status, - // not available - transaction: null, - transaction_outcome: txResult.transaction, - receipts_outcome: txResult.receipts - }; - } - return txResult; -} -exports.adaptTransactionResult = adaptTransactionResult; }).call(this,require("buffer").Buffer) },{"buffer":43}],22:[function(require,module,exports){ diff --git a/dist/near-api-js.min.js b/dist/near-api-js.min.js index 7e84cd2861..c38c0bb194 100644 --- a/dist/near-api-js.min.js +++ b/dist/near-api-js.min.js @@ -13,7 +13,7 @@ require("error-polyfill"),window.nearApi=require("./lib/browser-index"),window.B },{"./utils/web":34}],4:[function(require,module,exports){ (function (process,Buffer){ -"use strict";var __importDefault=this&&this.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(exports,"__esModule",{value:!0}),exports.AccountMultisig=exports.MULTISIG_CONFIRM_METHODS=exports.MULTISIG_VIEW_METHODS=exports.MULTISIG_CHANGE_METHODS=exports.MULTISIG_DEPOSIT=exports.MULTISIG_GAS=exports.MULTISIG_ALLOWANCE=void 0;const bn_js_1=__importDefault(require("bn.js")),account_1=require("./account"),contract_1=require("./contract"),format_1=require("./utils/format"),key_pair_1=require("./utils/key_pair"),transaction_1=require("./transaction"),NETWORK_ID=process.env.REACT_APP_NETWORK_ID||"default",CONTRACT_HELPER_URL=process.env.CONTRACT_HELPER_URL||"https://helper.testnet.near.org";exports.MULTISIG_ALLOWANCE=new bn_js_1.default(process.env.MULTISIG_ALLOWANCE||format_1.parseNearAmount("1")),exports.MULTISIG_GAS=new bn_js_1.default(process.env.MULTISIG_GAS||"100000000000000"),exports.MULTISIG_DEPOSIT=new bn_js_1.default("0"),exports.MULTISIG_CHANGE_METHODS=["add_request","add_request_and_confirm","delete_request","confirm"],exports.MULTISIG_VIEW_METHODS=["get_request_nonce","list_request_ids"],exports.MULTISIG_CONFIRM_METHODS=["confirm"];class AccountMultisig extends account_1.Account{constructor(t,e){super(t,e),this.contract=getContract(this)}async addKey(t,e,n,s){return e?super.addKey(t,e,exports.MULTISIG_CHANGE_METHODS.join(),exports.MULTISIG_ALLOWANCE):super.addKey(t)}async signAndSendTransaction(t,e){const{accountId:n}=this;if(this.isDeleteAction(e))return await super.signAndSendTransaction(n,e);await this.deleteUnconfirmedRequests();const s=await this.getRequestNonce();this.setRequest({accountId:n,requestId:s,actions:e});const o=new Uint8Array((new TextEncoder).encode(JSON.stringify({request:{receiver_id:t,actions:convertActions(e,n,t)}})));return await super.signAndSendTransaction(n,[transaction_1.functionCall("add_request_and_confirm",o,exports.MULTISIG_GAS,exports.MULTISIG_DEPOSIT)])}async signAndSendTransactions(t){for(let{receiverId:e,actions:n}of t)await this.signAndSendTransaction(e,n)}async deployMultisig(t){const{accountId:e}=this,n=(await this.getAccessKeys()).map(t=>t.public_key),s=(await this.getRecoveryMethods()).data.filter(({kind:t,publicKey:e})=>"phrase"===t&&null!==e&&n.includes(e)).map(t=>t.publicKey),o=n.filter(t=>!s.includes(t)).map(toPK),r=toPK((await this.postSignedJson("/2fa/getAccessKey",{accountId:e})).publicKey),i=new Uint8Array((new TextEncoder).encode(JSON.stringify({num_confirmations:2}))),a=[...o.map(t=>transaction_1.deleteKey(t)),...o.map(t=>transaction_1.addKey(t,transaction_1.functionCallAccessKey(e,exports.MULTISIG_CHANGE_METHODS,null))),transaction_1.addKey(r,transaction_1.functionCallAccessKey(e,exports.MULTISIG_CONFIRM_METHODS,null)),transaction_1.deployContract(t),transaction_1.functionCall("new",i,exports.MULTISIG_GAS,exports.MULTISIG_DEPOSIT)];return console.log("deploying multisig contract for",e),await super.signAndSendTransaction(e,a)}async deleteUnconfirmedRequests(){const{contract:t}=this,e=await this.getRequestIds();for(const n of e)try{await t.delete_request({request_id:n})}catch(t){console.warn(t)}}async getRequestNonce(){return this.contract.get_request_nonce()}async getRequestIds(){return this.contract.list_request_ids()}isDeleteAction(t){return t&&t[0]&&t[0].functionCall&&"delete_request"===t[0].functionCall.methodName}getRequest(){return JSON.parse(localStorage.getItem("__multisigRequest")||"{}")}setRequest(t){localStorage.setItem("__multisigRequest",JSON.stringify(t))}async sendRequestCode(){const{accountId:t}=this,{requestId:e,actions:n}=this.getRequest();if(this.isDeleteAction(n))return;const s=await this.get2faMethod();return await this.postSignedJson("/2fa/send",{accountId:t,method:s,requestId:e}),e}async verifyRequestCode(t){const{accountId:e}=this,n=this.getRequest();if(!n)throw new Error("no request pending");const{requestId:s}=n;return await this.postSignedJson("/2fa/verify",{accountId:e,securityCode:t,requestId:s})}async getRecoveryMethods(){const{accountId:t}=this;return{accountId:t,data:await this.postSignedJson("/account/recoveryMethods",{accountId:t})}}async get2faMethod(){let{data:t}=await this.getRecoveryMethods();if(t&&t.length&&(t=t.find(t=>0===t.kind.indexOf("2fa-"))),!t)return null;const{kind:e,detail:n}=t;return{kind:e,detail:n}}async signatureFor(){const{accountId:t}=this,e=String((await this.connection.provider.status()).sync_info.latest_block_height),n=await this.connection.signer.signMessage(Buffer.from(e),t,NETWORK_ID);return{blockNumber:e,blockNumberSignature:Buffer.from(n.signature).toString("base64")}}async postSignedJson(t,e){const n=await fetch(CONTRACT_HELPER_URL+t,{method:"POST",body:JSON.stringify({...e,...await this.signatureFor()}),headers:{"Content-type":"application/json; charset=utf-8"}});if(!n.ok)throw new Error(n.status+": "+await n.text());return 204===n.status?null:await n.json()}}exports.AccountMultisig=AccountMultisig;const toPK=t=>key_pair_1.PublicKey.from(t),convertPKForContract=t=>t.toString().replace("ed25519:",""),getContract=t=>new contract_1.Contract(t,t.accountId,{viewMethods:exports.MULTISIG_VIEW_METHODS,changeMethods:exports.MULTISIG_CHANGE_METHODS}),convertActions=(t,e,n)=>t.map(t=>{const s=t.enum,{gas:o,publicKey:r,methodName:i,args:a,deposit:c,accessKey:u,code:d}=t[s],_={type:s[0].toUpperCase()+s.substr(1),gas:o&&o.toString()||void 0,public_key:r&&convertPKForContract(r)||void 0,method_name:i,args:a&&Buffer.from(a).toString("base64")||void 0,code:d&&Buffer.from(d).toString("base64")||void 0,amount:c&&c.toString()||void 0,deposit:c&&c.toString()||void 0,permission:void 0};if(u&&(n===e&&"fullAccess"!==u.permission.enum&&(_.permission={receiver_id:e,allowance:exports.MULTISIG_ALLOWANCE.toString(),method_names:exports.MULTISIG_CHANGE_METHODS}),"functionCall"===u.permission.enum)){const{receiverId:t,methodNames:e,allowance:n}=u.permission.functionCall;_.permission={receiver_id:t,allowance:n&&n.toString()||void 0,method_names:e}}return _}); +"use strict";var __importDefault=this&&this.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(exports,"__esModule",{value:!0}),exports.AccountMultisig=exports.MULTISIG_CONFIRM_METHODS=exports.MULTISIG_VIEW_METHODS=exports.MULTISIG_CHANGE_METHODS=exports.MULTISIG_DEPOSIT=exports.MULTISIG_GAS=exports.MULTISIG_ALLOWANCE=void 0;const bn_js_1=__importDefault(require("bn.js")),account_1=require("./account"),contract_1=require("./contract"),format_1=require("./utils/format"),key_pair_1=require("./utils/key_pair"),transaction_1=require("./transaction"),NETWORK_ID=process.env.REACT_APP_NETWORK_ID||"default",CONTRACT_HELPER_URL=process.env.CONTRACT_HELPER_URL||"https://helper.testnet.near.org";exports.MULTISIG_ALLOWANCE=new bn_js_1.default(process.env.MULTISIG_ALLOWANCE||format_1.parseNearAmount("1")),exports.MULTISIG_GAS=new bn_js_1.default(process.env.MULTISIG_GAS||"100000000000000"),exports.MULTISIG_DEPOSIT=new bn_js_1.default("0"),exports.MULTISIG_CHANGE_METHODS=["add_request","add_request_and_confirm","delete_request","confirm"],exports.MULTISIG_VIEW_METHODS=["get_request_nonce","list_request_ids"],exports.MULTISIG_CONFIRM_METHODS=["confirm"];const IS_BROWSER="undefined"!=typeof window;let __multisigRequest=null;class AccountMultisig extends account_1.Account{constructor(t,e){super(t,e),this.contract=getContract(this)}async addKey(t,e,n,s){return e?super.addKey(t,e,exports.MULTISIG_CHANGE_METHODS.join(),exports.MULTISIG_ALLOWANCE):super.addKey(t)}async signAndSendTransaction(t,e){const{accountId:n}=this;if(this.isDeleteAction(e))return await super.signAndSendTransaction(n,e);await this.deleteUnconfirmedRequests();const s=await this.getRequestNonce();this.setRequest({accountId:n,requestId:s,actions:e});const o=new Uint8Array((new TextEncoder).encode(JSON.stringify({request:{receiver_id:t,actions:convertActions(e,n,t)}})));return await super.signAndSendTransaction(n,[transaction_1.functionCall("add_request_and_confirm",o,exports.MULTISIG_GAS,exports.MULTISIG_DEPOSIT)])}async signAndSendTransactions(t){for(let{receiverId:e,actions:n}of t)await this.signAndSendTransaction(e,n)}async deployMultisig(t){const{accountId:e}=this,n=(await this.getAccessKeys()).map(t=>t.public_key),s=(await this.getRecoveryMethods()).data.filter(({kind:t,publicKey:e})=>"phrase"===t&&null!==e&&n.includes(e)).map(t=>t.publicKey),o=n.filter(t=>!s.includes(t)).map(toPK),r=toPK((await this.postSignedJson("/2fa/getAccessKey",{accountId:e})).publicKey),i=new Uint8Array((new TextEncoder).encode(JSON.stringify({num_confirmations:2}))),a=[...o.map(t=>transaction_1.deleteKey(t)),...o.map(t=>transaction_1.addKey(t,transaction_1.functionCallAccessKey(e,exports.MULTISIG_CHANGE_METHODS,null))),transaction_1.addKey(r,transaction_1.functionCallAccessKey(e,exports.MULTISIG_CONFIRM_METHODS,null)),transaction_1.deployContract(t),transaction_1.functionCall("new",i,exports.MULTISIG_GAS,exports.MULTISIG_DEPOSIT)];return console.log("deploying multisig contract for",e),await super.signAndSendTransaction(e,a)}async deleteUnconfirmedRequests(){const{contract:t}=this,e=await this.getRequestIds();for(const n of e)try{await t.delete_request({request_id:n})}catch(t){console.warn(t)}}async getRequestNonce(){return this.contract.get_request_nonce()}async getRequestIds(){return this.contract.list_request_ids()}isDeleteAction(t){return t&&t[0]&&t[0].functionCall&&"delete_request"===t[0].functionCall.methodName}getRequest(){return IS_BROWSER?JSON.parse(localStorage.getItem("__multisigRequest")||"{}"):__multisigRequest}setRequest(t){if(IS_BROWSER)return localStorage.setItem("__multisigRequest",JSON.stringify(t));__multisigRequest=t}async sendRequestCode(){const{accountId:t}=this,{requestId:e,actions:n}=this.getRequest();if(this.isDeleteAction(n))return;const s=await this.get2faMethod();return await this.postSignedJson("/2fa/send",{accountId:t,method:s,requestId:e}),e}async verifyRequestCode(t){const{accountId:e}=this,n=this.getRequest();if(!n)throw new Error("no request pending");const{requestId:s}=n;return await this.postSignedJson("/2fa/verify",{accountId:e,securityCode:t,requestId:s})}async getRecoveryMethods(){const{accountId:t}=this;return{accountId:t,data:await this.postSignedJson("/account/recoveryMethods",{accountId:t})}}async get2faMethod(){let{data:t}=await this.getRecoveryMethods();if(t&&t.length&&(t=t.find(t=>0===t.kind.indexOf("2fa-"))),!t)return null;const{kind:e,detail:n}=t;return{kind:e,detail:n}}async signatureFor(){const{accountId:t}=this,e=String((await this.connection.provider.status()).sync_info.latest_block_height),n=await this.connection.signer.signMessage(Buffer.from(e),t,NETWORK_ID);return{blockNumber:e,blockNumberSignature:Buffer.from(n.signature).toString("base64")}}async postSignedJson(t,e){const n=await fetch(CONTRACT_HELPER_URL+t,{method:"POST",body:JSON.stringify({...e,...await this.signatureFor()}),headers:{"Content-type":"application/json; charset=utf-8"}});if(!n.ok)throw new Error(n.status+": "+await n.text());return 204===n.status?null:await n.json()}}exports.AccountMultisig=AccountMultisig;const toPK=t=>key_pair_1.PublicKey.from(t),convertPKForContract=t=>t.toString().replace("ed25519:",""),getContract=t=>new contract_1.Contract(t,t.accountId,{viewMethods:exports.MULTISIG_VIEW_METHODS,changeMethods:exports.MULTISIG_CHANGE_METHODS}),convertActions=(t,e,n)=>t.map(t=>{const s=t.enum,{gas:o,publicKey:r,methodName:i,args:a,deposit:c,accessKey:u,code:d}=t[s],_={type:s[0].toUpperCase()+s.substr(1),gas:o&&o.toString()||void 0,public_key:r&&convertPKForContract(r)||void 0,method_name:i,args:a&&Buffer.from(a).toString("base64")||void 0,code:d&&Buffer.from(d).toString("base64")||void 0,amount:c&&c.toString()||void 0,deposit:c&&c.toString()||"0",permission:void 0};if(u&&(n===e&&"fullAccess"!==u.permission.enum&&(_.permission={receiver_id:e,allowance:exports.MULTISIG_ALLOWANCE.toString(),method_names:exports.MULTISIG_CHANGE_METHODS}),"functionCall"===u.permission.enum)){const{receiverId:t,methodNames:e,allowance:n}=u.permission.functionCall;_.permission={receiver_id:t,allowance:n&&n.toString()||void 0,method_names:e}}return _}); }).call(this,require('_process'),require("buffer").Buffer) },{"./account":2,"./contract":8,"./transaction":24,"./utils/format":28,"./utils/key_pair":30,"_process":74,"bn.js":39,"buffer":44}],5:[function(require,module,exports){ @@ -728,12 +728,12 @@ module.exports={ },{"./json-rpc-provider":20,"./provider":21}],20:[function(require,module,exports){ (function (Buffer){ -"use strict";var __importDefault=this&&this.__importDefault||function(r){return r&&r.__esModule?r:{default:r}};Object.defineProperty(exports,"__esModule",{value:!0}),exports.JsonRpcProvider=exports.ErrorContext=exports.TypedError=void 0;const depd_1=__importDefault(require("depd")),provider_1=require("./provider"),web_1=require("../utils/web"),errors_1=require("../utils/errors");Object.defineProperty(exports,"TypedError",{enumerable:!0,get:function(){return errors_1.TypedError}}),Object.defineProperty(exports,"ErrorContext",{enumerable:!0,get:function(){return errors_1.ErrorContext}});const serialize_1=require("../utils/serialize"),rpc_errors_1=require("../utils/rpc_errors");let _nextId=123;class JsonRpcProvider extends provider_1.Provider{constructor(r){super(),this.connection={url:r}}async getNetwork(){return{name:"test",chainId:"test"}}async status(){return this.sendJsonRpc("status",[])}async sendTransaction(r){const e=r.encode();return this.sendJsonRpc("broadcast_tx_commit",[Buffer.from(e).toString("base64")]).then(provider_1.adaptTransactionResult)}async txStatus(r,e){return this.sendJsonRpc("tx",[serialize_1.base_encode(r),e]).then(provider_1.adaptTransactionResult)}async query(r,e){const t=await this.sendJsonRpc("query",[r,e]);if(t&&t.error)throw new Error(`Querying ${r} failed: ${t.error}.\n${JSON.stringify(t,null,2)}`);return t}async block(r){const{finality:e}=r;let{blockId:t}=r;if("object"!=typeof r){depd_1.default("JsonRpcProvider.block(blockId)")("use `block({ blockId })` or `block({ finality })` instead"),t=r}return this.sendJsonRpc("block",{block_id:t,finality:e})}async chunk(r){return this.sendJsonRpc("chunk",[r])}async validators(r){return this.sendJsonRpc("validators",[r])}async experimental_genesisConfig(){return await this.sendJsonRpc("EXPERIMENTAL_genesis_config",[])}async experimental_lightClientProof(r){return depd_1.default("JsonRpcProvider.experimental_lightClientProof(request)")("use `lightClientProof` instead"),await this.lightClientProof(r)}async lightClientProof(r){return await this.sendJsonRpc("EXPERIMENTAL_light_client_proof",r)}async sendJsonRpc(r,e){const t={method:r,params:e,id:_nextId++,jsonrpc:"2.0"},o=await web_1.fetchJson(this.connection,JSON.stringify(t));if(o.error){if("object"==typeof o.error.data)throw"string"==typeof o.error.data.error_message&&"string"==typeof o.error.data.error_type?new errors_1.TypedError(o.error.data.error_message,o.error.data.error_type):rpc_errors_1.parseRpcError(o.error.data);{const r=`[${o.error.code}] ${o.error.message}: ${o.error.data}`;throw"Timeout"===o.error.data||r.includes("Timeout error")?new errors_1.TypedError("send_tx_commit has timed out.","TimeoutError"):new errors_1.TypedError(r)}}return o.result}}exports.JsonRpcProvider=JsonRpcProvider; +"use strict";var __importDefault=this&&this.__importDefault||function(r){return r&&r.__esModule?r:{default:r}};Object.defineProperty(exports,"__esModule",{value:!0}),exports.JsonRpcProvider=exports.ErrorContext=exports.TypedError=void 0;const depd_1=__importDefault(require("depd")),provider_1=require("./provider"),web_1=require("../utils/web"),errors_1=require("../utils/errors");Object.defineProperty(exports,"TypedError",{enumerable:!0,get:function(){return errors_1.TypedError}}),Object.defineProperty(exports,"ErrorContext",{enumerable:!0,get:function(){return errors_1.ErrorContext}});const serialize_1=require("../utils/serialize"),rpc_errors_1=require("../utils/rpc_errors");let _nextId=123;class JsonRpcProvider extends provider_1.Provider{constructor(r){super(),this.connection={url:r}}async getNetwork(){return{name:"test",chainId:"test"}}async status(){return this.sendJsonRpc("status",[])}async sendTransaction(r){const e=r.encode();return this.sendJsonRpc("broadcast_tx_commit",[Buffer.from(e).toString("base64")])}async txStatus(r,e){return this.sendJsonRpc("tx",[serialize_1.base_encode(r),e])}async query(r,e){const t=await this.sendJsonRpc("query",[r,e]);if(t&&t.error)throw new Error(`Querying ${r} failed: ${t.error}.\n${JSON.stringify(t,null,2)}`);return t}async block(r){const{finality:e}=r;let{blockId:t}=r;if("object"!=typeof r){depd_1.default("JsonRpcProvider.block(blockId)")("use `block({ blockId })` or `block({ finality })` instead"),t=r}return this.sendJsonRpc("block",{block_id:t,finality:e})}async chunk(r){return this.sendJsonRpc("chunk",[r])}async validators(r){return this.sendJsonRpc("validators",[r])}async experimental_genesisConfig(){return await this.sendJsonRpc("EXPERIMENTAL_genesis_config",[])}async experimental_lightClientProof(r){return depd_1.default("JsonRpcProvider.experimental_lightClientProof(request)")("use `lightClientProof` instead"),await this.lightClientProof(r)}async lightClientProof(r){return await this.sendJsonRpc("EXPERIMENTAL_light_client_proof",r)}async sendJsonRpc(r,e){const t={method:r,params:e,id:_nextId++,jsonrpc:"2.0"},o=await web_1.fetchJson(this.connection,JSON.stringify(t));if(o.error){if("object"==typeof o.error.data)throw"string"==typeof o.error.data.error_message&&"string"==typeof o.error.data.error_type?new errors_1.TypedError(o.error.data.error_message,o.error.data.error_type):rpc_errors_1.parseRpcError(o.error.data);{const r=`[${o.error.code}] ${o.error.message}: ${o.error.data}`;throw"Timeout"===o.error.data||r.includes("Timeout error")?new errors_1.TypedError("send_tx_commit has timed out.","TimeoutError"):new errors_1.TypedError(r)}}return o.result}}exports.JsonRpcProvider=JsonRpcProvider; }).call(this,require("buffer").Buffer) },{"../utils/errors":26,"../utils/rpc_errors":32,"../utils/serialize":33,"../utils/web":34,"./provider":21,"buffer":44,"depd":51}],21:[function(require,module,exports){ (function (Buffer){ -"use strict";var ExecutionStatusBasic,FinalExecutionStatusBasic,IdType;Object.defineProperty(exports,"__esModule",{value:!0}),exports.adaptTransactionResult=exports.getTransactionLastResult=exports.Provider=exports.IdType=exports.FinalExecutionStatusBasic=exports.ExecutionStatusBasic=void 0,function(t){t.Unknown="Unknown",t.Pending="Pending",t.Failure="Failure"}(ExecutionStatusBasic=exports.ExecutionStatusBasic||(exports.ExecutionStatusBasic={})),function(t){t.NotStarted="NotStarted",t.Started="Started",t.Failure="Failure"}(FinalExecutionStatusBasic=exports.FinalExecutionStatusBasic||(exports.FinalExecutionStatusBasic={})),function(t){t.Transaction="transaction",t.Receipt="receipt"}(IdType=exports.IdType||(exports.IdType={}));class Provider{}function getTransactionLastResult(t){if("object"==typeof t.status&&"string"==typeof t.status.SuccessValue){const e=Buffer.from(t.status.SuccessValue,"base64").toString();try{return JSON.parse(e)}catch(t){return e}}return null}function adaptTransactionResult(t){return"receipts"in t&&(t={status:t.status,transaction:null,transaction_outcome:t.transaction,receipts_outcome:t.receipts}),t}exports.Provider=Provider,exports.getTransactionLastResult=getTransactionLastResult,exports.adaptTransactionResult=adaptTransactionResult; +"use strict";var ExecutionStatusBasic,FinalExecutionStatusBasic,IdType;Object.defineProperty(exports,"__esModule",{value:!0}),exports.getTransactionLastResult=exports.Provider=exports.IdType=exports.FinalExecutionStatusBasic=exports.ExecutionStatusBasic=void 0,function(t){t.Unknown="Unknown",t.Pending="Pending",t.Failure="Failure"}(ExecutionStatusBasic=exports.ExecutionStatusBasic||(exports.ExecutionStatusBasic={})),function(t){t.NotStarted="NotStarted",t.Started="Started",t.Failure="Failure"}(FinalExecutionStatusBasic=exports.FinalExecutionStatusBasic||(exports.FinalExecutionStatusBasic={})),function(t){t.Transaction="transaction",t.Receipt="receipt"}(IdType=exports.IdType||(exports.IdType={}));class Provider{}function getTransactionLastResult(t){if("object"==typeof t.status&&"string"==typeof t.status.SuccessValue){const e=Buffer.from(t.status.SuccessValue,"base64").toString();try{return JSON.parse(e)}catch(t){return e}}return null}exports.Provider=Provider,exports.getTransactionLastResult=getTransactionLastResult; }).call(this,require("buffer").Buffer) },{"buffer":44}],22:[function(require,module,exports){ diff --git a/lib/account_multisig.js b/lib/account_multisig.js index b08a334ec5..7b3702826a 100644 --- a/lib/account_multisig.js +++ b/lib/account_multisig.js @@ -10,6 +10,11 @@ const contract_1 = require("./contract"); const format_1 = require("./utils/format"); const key_pair_1 = require("./utils/key_pair"); const transaction_1 = require("./transaction"); +const IS_BROWSER = typeof window !== 'undefined'; +let fetch = IS_BROWSER && window.fetch; +if (!IS_BROWSER) { + fetch = require('node-fetch'); +} const NETWORK_ID = process.env.REACT_APP_NETWORK_ID || 'default'; const CONTRACT_HELPER_URL = process.env.CONTRACT_HELPER_URL || 'https://helper.testnet.near.org'; exports.MULTISIG_ALLOWANCE = new bn_js_1.default(process.env.MULTISIG_ALLOWANCE || format_1.parseNearAmount('1')); @@ -19,6 +24,8 @@ exports.MULTISIG_CHANGE_METHODS = ['add_request', 'add_request_and_confirm', 'de exports.MULTISIG_VIEW_METHODS = ['get_request_nonce', 'list_request_ids']; exports.MULTISIG_CONFIRM_METHODS = ['confirm']; ; +// in memory request cache for node w/o localStorage +let __multisigRequest = null; class AccountMultisig extends account_1.Account { constructor(connection, accountId) { super(connection, accountId); @@ -96,10 +103,16 @@ class AccountMultisig extends account_1.Account { return actions && actions[0] && actions[0].functionCall && actions[0].functionCall.methodName === 'delete_request'; } getRequest() { - return JSON.parse(localStorage.getItem(`__multisigRequest`) || `{}`); + if (IS_BROWSER) { + return JSON.parse(localStorage.getItem(`__multisigRequest`) || `{}`); + } + return __multisigRequest; } setRequest(data) { - localStorage.setItem(`__multisigRequest`, JSON.stringify(data)); + if (IS_BROWSER) { + return localStorage.setItem(`__multisigRequest`, JSON.stringify(data)); + } + __multisigRequest = data; } // default helpers for CH async sendRequestCode() { @@ -192,7 +205,7 @@ const convertActions = (actions, accountId, receiverId) => actions.map((a) => { args: (args && Buffer.from(args).toString('base64')) || undefined, code: (code && Buffer.from(code).toString('base64')) || undefined, amount: (deposit && deposit.toString()) || undefined, - deposit: (deposit && deposit.toString()) || undefined, + deposit: (deposit && deposit.toString()) || '0', permission: undefined, }; if (accessKey) { diff --git a/package.json b/package.json index b242907523..8a2c79feda 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "http-errors": "^1.7.2", "js-sha256": "^0.9.0", "mustache": "^4.0.0", - "node-fetch": "^2.3.0", + "node-fetch": "^2.6.1", "text-encoding-utf-8": "^1.0.2", "tweetnacl": "^1.0.1" }, diff --git a/src/account.ts b/src/account.ts index 7508b1e4ea..1c3e42b65e 100644 --- a/src/account.ts +++ b/src/account.ts @@ -124,6 +124,7 @@ export class Account { const status = await this.connection.provider.status(); const nonce = ++accessKey.nonce; + [txHash, signedTx] = await signTransaction( receiverId, nonce, actions, base_decode(status.sync_info.latest_block_hash), this.connection.signer, this.accountId, this.connection.networkId ); diff --git a/src/account_multisig.ts b/src/account_multisig.ts index f0bd7dabb1..f74056d5a5 100644 --- a/src/account_multisig.ts +++ b/src/account_multisig.ts @@ -9,6 +9,13 @@ import { PublicKey } from './utils/key_pair'; import { Action, addKey, deleteKey, deployContract, functionCall, functionCallAccessKey } from './transaction'; import { FinalExecutionOutcome } from './providers'; +const IS_BROWSER = typeof window !== 'undefined' + +let fetch = IS_BROWSER && window.fetch +if (!IS_BROWSER) { + fetch = require('node-fetch'); +} + const NETWORK_ID = process.env.REACT_APP_NETWORK_ID || 'default' const CONTRACT_HELPER_URL = process.env.CONTRACT_HELPER_URL || 'https://helper.testnet.near.org'; @@ -19,12 +26,16 @@ export const MULTISIG_CHANGE_METHODS = ['add_request', 'add_request_and_confirm' export const MULTISIG_VIEW_METHODS = ['get_request_nonce', 'list_request_ids']; export const MULTISIG_CONFIRM_METHODS = ['confirm']; + interface MultisigContract { get_request_nonce(): any, list_request_ids(): any, delete_request({ request_id: Number }): any, }; +// in memory request cache for node w/o localStorage +let __multisigRequest = null + export class AccountMultisig extends Account { public contract: MultisigContract; public pendingRequest: any; @@ -118,11 +129,17 @@ export class AccountMultisig extends Account { } getRequest() { - return JSON.parse(localStorage.getItem(`__multisigRequest`) || `{}`) + if (IS_BROWSER) { + return JSON.parse(localStorage.getItem(`__multisigRequest`) || `{}`) + } + return __multisigRequest } setRequest(data) { - localStorage.setItem(`__multisigRequest`, JSON.stringify(data)) + if (IS_BROWSER) { + return localStorage.setItem(`__multisigRequest`, JSON.stringify(data)) + } + __multisigRequest = data } // default helpers for CH @@ -223,7 +240,7 @@ const convertActions = (actions, accountId, receiverId) => actions.map((a) => { args: (args && Buffer.from(args).toString('base64')) || undefined, code: (code && Buffer.from(code).toString('base64')) || undefined, amount: (deposit && deposit.toString()) || undefined, - deposit: (deposit && deposit.toString()) || undefined, + deposit: (deposit && deposit.toString()) || '0', permission: undefined, }; if (accessKey) { diff --git a/yarn.lock b/yarn.lock index 3f98a02317..676e342c7d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3696,7 +3696,7 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== -node-fetch@^2.3.0: +node-fetch@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== From 7d929918e51fe6721e8df40e1cd3e3b7b08013b2 Mon Sep 17 00:00:00 2001 From: Matt Lockyer Date: Thu, 1 Oct 2020 11:09:13 -0700 Subject: [PATCH 2/3] cleaned up --- lib/account_multisig.d.ts | 7 +++--- lib/account_multisig.js | 47 ++++++++++++++----------------------- src/account_multisig.ts | 49 ++++++++++++++------------------------- 3 files changed, 40 insertions(+), 63 deletions(-) diff --git a/lib/account_multisig.d.ts b/lib/account_multisig.d.ts index 529e0c8e31..69d33bfa8b 100644 --- a/lib/account_multisig.d.ts +++ b/lib/account_multisig.d.ts @@ -4,6 +4,7 @@ import { Connection } from './connection'; import { PublicKey } from './utils/key_pair'; import { Action } from './transaction'; import { FinalExecutionOutcome } from './providers'; +export declare const MULTISIG_STORAGE_KEY = "__multisigRequest"; export declare const MULTISIG_ALLOWANCE: BN; export declare const MULTISIG_GAS: BN; export declare const MULTISIG_DEPOSIT: BN; @@ -19,8 +20,8 @@ interface MultisigContract { } export declare class AccountMultisig extends Account { contract: MultisigContract; - pendingRequest: any; - constructor(connection: Connection, accountId: string); + storage: any; + constructor(connection: Connection, accountId: string, storage: any); addKey(publicKey: string | PublicKey, contractId?: string, methodName?: string, amount?: BN): Promise; signAndSendTransaction(receiverId: string, actions: Action[]): Promise; signAndSendTransactions(transactions: any): Promise; @@ -30,7 +31,7 @@ export declare class AccountMultisig extends Account { getRequestIds(): Promise; isDeleteAction(actions: any): Boolean; getRequest(): any; - setRequest(data: any): void; + setRequest(data: any): any; sendRequestCode(): Promise; verifyRequestCode(securityCode: string): Promise; getRecoveryMethods(): Promise<{ diff --git a/lib/account_multisig.js b/lib/account_multisig.js index 7b3702826a..43af6c5b54 100644 --- a/lib/account_multisig.js +++ b/lib/account_multisig.js @@ -3,20 +3,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.AccountMultisig = exports.MULTISIG_CONFIRM_METHODS = exports.MULTISIG_VIEW_METHODS = exports.MULTISIG_CHANGE_METHODS = exports.MULTISIG_DEPOSIT = exports.MULTISIG_GAS = exports.MULTISIG_ALLOWANCE = void 0; +exports.AccountMultisig = exports.MULTISIG_CONFIRM_METHODS = exports.MULTISIG_VIEW_METHODS = exports.MULTISIG_CHANGE_METHODS = exports.MULTISIG_DEPOSIT = exports.MULTISIG_GAS = exports.MULTISIG_ALLOWANCE = exports.MULTISIG_STORAGE_KEY = void 0; const bn_js_1 = __importDefault(require("bn.js")); const account_1 = require("./account"); const contract_1 = require("./contract"); const format_1 = require("./utils/format"); const key_pair_1 = require("./utils/key_pair"); const transaction_1 = require("./transaction"); -const IS_BROWSER = typeof window !== 'undefined'; -let fetch = IS_BROWSER && window.fetch; -if (!IS_BROWSER) { - fetch = require('node-fetch'); -} +const web_1 = require("./utils/web"); const NETWORK_ID = process.env.REACT_APP_NETWORK_ID || 'default'; const CONTRACT_HELPER_URL = process.env.CONTRACT_HELPER_URL || 'https://helper.testnet.near.org'; +exports.MULTISIG_STORAGE_KEY = '__multisigRequest'; exports.MULTISIG_ALLOWANCE = new bn_js_1.default(process.env.MULTISIG_ALLOWANCE || format_1.parseNearAmount('1')); exports.MULTISIG_GAS = new bn_js_1.default(process.env.MULTISIG_GAS || '100000000000000'); exports.MULTISIG_DEPOSIT = new bn_js_1.default('0'); @@ -25,10 +22,13 @@ exports.MULTISIG_VIEW_METHODS = ['get_request_nonce', 'list_request_ids']; exports.MULTISIG_CONFIRM_METHODS = ['confirm']; ; // in memory request cache for node w/o localStorage -let __multisigRequest = null; +let storageFallback = { + [exports.MULTISIG_STORAGE_KEY]: null +}; class AccountMultisig extends account_1.Account { - constructor(connection, accountId) { + constructor(connection, accountId, storage) { super(connection, accountId); + this.storage = storage; this.contract = getContract(this); } async addKey(publicKey, contractId, methodName, amount) { @@ -103,16 +103,16 @@ class AccountMultisig extends account_1.Account { return actions && actions[0] && actions[0].functionCall && actions[0].functionCall.methodName === 'delete_request'; } getRequest() { - if (IS_BROWSER) { - return JSON.parse(localStorage.getItem(`__multisigRequest`) || `{}`); + if (this.storage) { + return JSON.parse(this.storage.getItem(exports.MULTISIG_STORAGE_KEY) || `{}`); } - return __multisigRequest; + return storageFallback[exports.MULTISIG_STORAGE_KEY]; } setRequest(data) { - if (IS_BROWSER) { - return localStorage.setItem(`__multisigRequest`, JSON.stringify(data)); + if (this.storage) { + return this.storage.setItem(exports.MULTISIG_STORAGE_KEY, JSON.stringify(data)); } - __multisigRequest = data; + storageFallback[exports.MULTISIG_STORAGE_KEY] = data; } // default helpers for CH async sendRequestCode() { @@ -167,21 +167,10 @@ class AccountMultisig extends account_1.Account { return { blockNumber, blockNumberSignature }; } async postSignedJson(path, body) { - const response = await fetch(CONTRACT_HELPER_URL + path, { - method: 'POST', - body: JSON.stringify({ - ...body, - ...(await this.signatureFor()) - }), - headers: { 'Content-type': 'application/json; charset=utf-8' } - }); - if (!response.ok) { - throw new Error(response.status + ': ' + await response.text()); - } - if (response.status === 204) { - return null; - } - return await response.json(); + return await web_1.fetchJson(CONTRACT_HELPER_URL + path, JSON.stringify({ + ...body, + ...(await this.signatureFor()) + })); } } exports.AccountMultisig = AccountMultisig; diff --git a/src/account_multisig.ts b/src/account_multisig.ts index f74056d5a5..35b3de2a19 100644 --- a/src/account_multisig.ts +++ b/src/account_multisig.ts @@ -8,17 +8,12 @@ import { parseNearAmount } from './utils/format'; import { PublicKey } from './utils/key_pair'; import { Action, addKey, deleteKey, deployContract, functionCall, functionCallAccessKey } from './transaction'; import { FinalExecutionOutcome } from './providers'; - -const IS_BROWSER = typeof window !== 'undefined' - -let fetch = IS_BROWSER && window.fetch -if (!IS_BROWSER) { - fetch = require('node-fetch'); -} +import { fetchJson } from './utils/web'; const NETWORK_ID = process.env.REACT_APP_NETWORK_ID || 'default' const CONTRACT_HELPER_URL = process.env.CONTRACT_HELPER_URL || 'https://helper.testnet.near.org'; +export const MULTISIG_STORAGE_KEY = '__multisigRequest' export const MULTISIG_ALLOWANCE = new BN(process.env.MULTISIG_ALLOWANCE || parseNearAmount('1')); export const MULTISIG_GAS = new BN(process.env.MULTISIG_GAS || '100000000000000'); export const MULTISIG_DEPOSIT = new BN('0'); @@ -34,14 +29,17 @@ interface MultisigContract { }; // in memory request cache for node w/o localStorage -let __multisigRequest = null +let storageFallback = { + [MULTISIG_STORAGE_KEY]: null +} export class AccountMultisig extends Account { public contract: MultisigContract; - public pendingRequest: any; + public storage: any; - constructor(connection: Connection, accountId: string) { + constructor(connection: Connection, accountId: string, storage: any) { super(connection, accountId); + this.storage = storage; this.contract = getContract(this); } @@ -129,17 +127,17 @@ export class AccountMultisig extends Account { } getRequest() { - if (IS_BROWSER) { - return JSON.parse(localStorage.getItem(`__multisigRequest`) || `{}`) + if (this.storage) { + return JSON.parse(this.storage.getItem(MULTISIG_STORAGE_KEY) || `{}`) } - return __multisigRequest + return storageFallback[MULTISIG_STORAGE_KEY] } setRequest(data) { - if (IS_BROWSER) { - return localStorage.setItem(`__multisigRequest`, JSON.stringify(data)) + if (this.storage) { + return this.storage.setItem(MULTISIG_STORAGE_KEY, JSON.stringify(data)) } - __multisigRequest = data + storageFallback[MULTISIG_STORAGE_KEY] = data } // default helpers for CH @@ -200,21 +198,10 @@ export class AccountMultisig extends Account { } async postSignedJson(path, body) { - const response = await fetch(CONTRACT_HELPER_URL + path, { - method: 'POST', - body: JSON.stringify({ - ...body, - ...(await this.signatureFor()) - }), - headers: { 'Content-type': 'application/json; charset=utf-8' } - }); - if (!response.ok) { - throw new Error(response.status + ': ' + await response.text()); - } - if (response.status === 204) { - return null; - } - return await response.json(); + return await fetchJson(CONTRACT_HELPER_URL + path, JSON.stringify({ + ...body, + ...(await this.signatureFor()) + })); } } From 1b506384a3c38f67e4ba2f2ebb2cd78340a10f3e Mon Sep 17 00:00:00 2001 From: Matt Lockyer Date: Thu, 1 Oct 2020 11:35:05 -0700 Subject: [PATCH 3/3] update delete request console warning so not so loud --- lib/account_multisig.js | 2 +- src/account_multisig.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/account_multisig.js b/lib/account_multisig.js index 43af6c5b54..18dfcdfb35 100644 --- a/lib/account_multisig.js +++ b/lib/account_multisig.js @@ -88,7 +88,7 @@ class AccountMultisig extends account_1.Account { await contract.delete_request({ request_id }); } catch (e) { - console.warn(e); + console.warn("Attempt to delete an earlier request before 15 minutes failed. Will try again."); } } } diff --git a/src/account_multisig.ts b/src/account_multisig.ts index 35b3de2a19..c675382401 100644 --- a/src/account_multisig.ts +++ b/src/account_multisig.ts @@ -107,7 +107,7 @@ export class AccountMultisig extends Account { try { await contract.delete_request({ request_id }) } catch(e) { - console.warn(e) + console.warn("Attempt to delete an earlier request before 15 minutes failed. Will try again.") } } }