diff --git a/examples/src/clean-state.js b/examples/src/clean-state.js index 6d7a379e8..cb9600675 100644 --- a/examples/src/clean-state.js +++ b/examples/src/clean-state.js @@ -2,17 +2,17 @@ import { NearBindgen, call, view, near } from 'near-sdk-js' @NearBindgen({}) class CleanState { - @call + @call({}) clean({ keys }) { keys.forEach(key => near.storageRemove(key)) } - @call + @call({}) put({ key, value }) { near.storageWrite(key, value) } - @view + @view({}) get({ key }) { return near.storageRead(key) diff --git a/examples/src/counter.js b/examples/src/counter.js index 4d214a805..1f898a985 100644 --- a/examples/src/counter.js +++ b/examples/src/counter.js @@ -7,13 +7,13 @@ class Counter { this.count = 0 } - @call + @call({}) increase({ n = 1 }) { this.count += n near.log(`Counter increased to ${this.count}`) } - @call + @call({}) decrease({ n }) { // you can use default argument `n=1` too // this is to illustrate a npm dependency: lodash can be used @@ -25,7 +25,7 @@ class Counter { near.log(`Counter decreased to ${this.count}`) } - @view + @view({}) getCount() { return this.count } diff --git a/examples/src/counter.ts b/examples/src/counter.ts index 501416f9d..9ab98cb63 100644 --- a/examples/src/counter.ts +++ b/examples/src/counter.ts @@ -6,13 +6,13 @@ import { log } from './log' class Counter { count: number = 0; - @call + @call({}) increase({ n = 1 }: { n: number }) { this.count += n near.log(`Counter increased to ${this.count}`) } - @call + @call({}) decrease({ n }: { n: number }) { // you can use default argument `n=1` too // this is to illustrate a npm dependency: lodash can be used @@ -25,7 +25,7 @@ class Counter { log(`Counter decreased to ${this.count}`) } - @view + @view({}) getCount(): number { return this.count } diff --git a/examples/src/cross-contract-call.js b/examples/src/cross-contract-call.js index c99116897..40388903d 100644 --- a/examples/src/cross-contract-call.js +++ b/examples/src/cross-contract-call.js @@ -7,13 +7,13 @@ class OnCall { this.statusMessageContract = '' } - @initialize + @initialize({}) init({ statusMessageContract }) { this.personOnCall = "undefined" this.statusMessageContract = statusMessageContract } - @call + @call({}) set_person_on_call({ accountId }) { near.log(`Trying to set ${accountId} on-call`) const promise = near.promiseBatchCreate(this.statusMessageContract) @@ -21,12 +21,9 @@ class OnCall { near.promiseThen(promise, near.currentAccountId(), '_set_person_on_call_private', bytes(JSON.stringify({ accountId: accountId })), 0, 30000000000000); } - @call + @call({ privateFunction: true }) _set_person_on_call_private({ accountId }) { near.log(`_set_person_on_call_private called, accountId ${accountId}`) - if (near.currentAccountId() !== near.predecessorAccountId()) { - throw Error('Function can be used as a callback only') - } const status = JSON.parse(near.promiseResult(0)) near.log(`${accountId} status is ${status}`) if (status === 'AVAILABLE') { @@ -37,7 +34,7 @@ class OnCall { } } - @view + @view({}) person_on_call() { near.log(`Returning person on-call: ${this.personOnCall}`) return this.personOnCall diff --git a/examples/src/fungible-token-helper.js b/examples/src/fungible-token-helper.js index 68a076e41..fd2bc18a1 100644 --- a/examples/src/fungible-token-helper.js +++ b/examples/src/fungible-token-helper.js @@ -6,13 +6,13 @@ class FungibleTokenHelper { this.data = ""; } - @call + @call({}) ftOnTransfer({ senderId, amount, msg, receiverId }) { const concatString = `[${amount} from ${senderId} to ${receiverId}] ${msg} `; this.data = this.data.concat("", concatString); } - @view + @view({}) getContractData() { return this.data; } diff --git a/examples/src/fungible-token-lockable.js b/examples/src/fungible-token-lockable.js index 8eb235dc7..9b3c36d44 100644 --- a/examples/src/fungible-token-lockable.js +++ b/examples/src/fungible-token-lockable.js @@ -56,7 +56,7 @@ class LockableFungibleToken { this.totalSupply = 0 // Total supply of the all tokens } - @initialize + @initialize({}) init({ prefix, totalSupply }) { this.accounts = new LookupMap(prefix) this.totalSupply = totalSupply @@ -78,7 +78,7 @@ class LockableFungibleToken { this.accounts.set(accountId, account) } - @call + @call({}) setAllowance({ escrowAccountId, allowance }) { let ownerId = near.predecessorAccountId() if (escrowAccountId === ownerId) { @@ -94,7 +94,7 @@ class LockableFungibleToken { this.setAccount(ownerId, account) } - @call + @call({}) lock({ ownerId, lockAmount }) { if (lockAmount <= 0) { throw Error("Can't lock 0 or less tokens") @@ -124,7 +124,7 @@ class LockableFungibleToken { this.setAccount(ownerId, account) } - @call + @call({}) unlock({ ownerId, unlockAmount }) { if (unlockAmount <= 0) { throw Error("Can't unlock 0 or less tokens") @@ -151,7 +151,7 @@ class LockableFungibleToken { this.setAccount(ownerId, account) } - @call + @call({}) transferFrom({ ownerId, newOwnerId, amount }) { if (amount <= 0) { throw Error("Can't transfer 0 or less tokens") @@ -197,32 +197,32 @@ class LockableFungibleToken { this.setAccount(newOwnerId, newAccount) } - @call + @call({}) transfer({ newOwnerId, amount }) { this.transferFrom({ ownerId: near.predecessorAccountId(), newOwnerId, amount }) } - @view + @view({}) getTotalSupply() { return this.totalSupply } - @view + @view({}) getTotalBalance({ ownerId }) { return this.getAccount(ownerId).totalBalance() } - @view + @view({}) getUnlockedBalance({ ownerId }) { return this.getAccount(ownerId).balance } - @view + @view({}) getAllowance({ ownerId, escrowAccountId }) { return this.getAccount(ownerId).getAllowance(escrowAccountId) } - @view + @view({}) getLockedBalance({ ownerId, escrowAccountId }) { return this.getAccount(ownerId).getLockedBalance(escrowAccountId) } diff --git a/examples/src/fungible-token.js b/examples/src/fungible-token.js index 0c3c1f392..0cb6be8dc 100644 --- a/examples/src/fungible-token.js +++ b/examples/src/fungible-token.js @@ -16,7 +16,7 @@ class FungibleToken { this.totalSupply = 0 } - @initialize + @initialize({}) init({ prefix, totalSupply }) { this.accounts = new LookupMap(prefix) this.totalSupply = totalSupply @@ -49,13 +49,13 @@ class FungibleToken { this.internalDeposit({ accountId: receiverId, amount }) } - @call + @call({}) ftTransfer({ receiverId, amount, memo }) { let senderId = near.predecessorAccountId() this.internalTransfer({ senderId, receiverId, amount, memo }) } - @call + @call({}) ftTransferCall({ receiverId, amount, memo, msg }) { let senderId = near.predecessorAccountId() this.internalTransfer({ senderId, receiverId, amount, memo }); @@ -65,12 +65,12 @@ class FungibleToken { return near.promiseReturn(); } - @view + @view({}) ftTotalSupply() { return this.totalSupply } - @view + @view({}) ftBalanceOf({ accountId }) { return this.accounts.get(accountId) || '0' } diff --git a/examples/src/non-fungible-token-receiver.js b/examples/src/non-fungible-token-receiver.js index 3468c59c3..a7e57571a 100644 --- a/examples/src/non-fungible-token-receiver.js +++ b/examples/src/non-fungible-token-receiver.js @@ -6,12 +6,12 @@ class NftContract { this.nonFungibleTokenAccountId = '' } - @initialize + @initialize({}) init({ nonFungibleTokenAccountId }) { this.nonFungibleTokenAccountId = nonFungibleTokenAccountId } - @call + @call({}) nftOnTransfer({ senderId, previousOwnerId, tokenId, msg }) { near.log(`nftOnTransfer called, params: senderId: ${senderId}, previousOwnerId: ${previousOwnerId}, tokenId: ${tokenId}, msg: ${msg}`) assert( diff --git a/examples/src/non-fungible-token.js b/examples/src/non-fungible-token.js index 9d6d214a8..b9fb45c4f 100644 --- a/examples/src/non-fungible-token.js +++ b/examples/src/non-fungible-token.js @@ -14,7 +14,7 @@ class NftContract { this.owner_by_id = new LookupMap('a') } - @initialize + @initialize({}) init({ owner_id, owner_by_id_prefix }) { this.owner_id = owner_id this.owner_by_id = new LookupMap(owner_by_id_prefix) @@ -32,13 +32,13 @@ class NftContract { return owner_id } - @call + @call({}) nftTransfer({ receiver_id, token_id, approval_id, memo }) { let sender_id = near.predecessorAccountId() this.internalTransfer({ sender_id, receiver_id, token_id, approval_id, memo }) } - @call + @call({}) nftTransferCall({ receiver_id, token_id, approval_id, memo, msg }) { near.log(`nftTransferCall called, receiver_id ${receiver_id}, token_id ${token_id}`) let sender_id = near.predecessorAccountId() @@ -49,12 +49,9 @@ class NftContract { near.promiseThen(promise, near.currentAccountId(), '_nftResolveTransfer', bytes(JSON.stringify({ sender_id, receiver_id, token_id })), 0, 30000000000000); } - @call + @call({ privateFunction: true }) _nftResolveTransfer({ sender_id, receiver_id, token_id }) { near.log(`_nftResolveTransfer called, receiver_id ${receiver_id}, token_id ${token_id}`) - if (near.currentAccountId() == !near.predecessorAccountId()) { - throw Error('Function can be used as a callback only') - } const isTokenTransfered = JSON.parse(near.promiseResult(0)) near.log(`${token_id} ${isTokenTransfered ? 'was transfered' : 'was NOT transfered'}`) @@ -70,7 +67,7 @@ class NftContract { } } - @call + @call({}) nftMint({ token_id, token_owner_id, token_metadata }) { let sender_id = near.predecessorAccountId() assert(sender_id === this.owner_id, "Unauthorized") @@ -81,7 +78,7 @@ class NftContract { return new Token(token_id, token_owner_id) } - @view + @view({}) nftToken({ token_id }) { let owner_id = this.owner_by_id.get(token_id) if (owner_id === null) { diff --git a/examples/src/parking-lot.ts b/examples/src/parking-lot.ts index 5aa12c8dc..023eff757 100644 --- a/examples/src/parking-lot.ts +++ b/examples/src/parking-lot.ts @@ -37,7 +37,7 @@ class ParkingLot { this.cars = new LookupMap('a'); } - @call + @call({}) addCar({ name, id, color, price, engineHp }: { name: string, id: number, color: string, price: number, engineHp: number }) { // args can be json arguments only, they cannot be of a JS/TS class like following, unless override NearContract.deserializeArgs method. // addCar({ name, specs }: { name: string, specs: CarSpecs }) { @@ -47,19 +47,19 @@ class ParkingLot { this.cars.set(name, car) } - @call + @call({}) removeCar({ name }: { name: string }) { near.log(`removeCar() called, name: ${name}`) this.cars.remove(name) } - @view + @view({}) getCarSpecs({ name }: { name: string }) { near.log(`getCarSpecs() called, name: ${name}`) return this.cars.get(name) } - @view + @view({}) runCar({ name }: { name: string }) { /* We are getting plain carSpecs object from the storage. It needs to be converted to the class object in order to execute engine.run() function.*/ diff --git a/examples/src/status-message-collections.js b/examples/src/status-message-collections.js index bc080d41b..f84dddfee 100644 --- a/examples/src/status-message-collections.js +++ b/examples/src/status-message-collections.js @@ -7,7 +7,7 @@ class StatusMessage { this.uniqueValues = new LookupSet('b') } - @call + @call({}) set_status({ message }) { let account_id = near.signerAccountId() near.log(`${account_id} set_status with message ${message}`) @@ -15,19 +15,19 @@ class StatusMessage { this.uniqueValues.set(message) } - @view + @view({}) get_status({ account_id }) { near.log(`get_status for account_id ${account_id}`) return this.records.get(account_id) } - @view + @view({}) has_status({ message }) { // used for test LookupMap return this.uniqueValues.contains(message) } - @view + @view({}) get_all_statuses() { // used for test UnorderedMap return this.records.toArray() diff --git a/examples/src/status-message.js b/examples/src/status-message.js index caf37f2d8..7ec2168fa 100644 --- a/examples/src/status-message.js +++ b/examples/src/status-message.js @@ -6,14 +6,14 @@ class StatusMessage { this.records = {} } - @call + @call({}) set_status({ message }) { let account_id = near.signerAccountId() near.log(`${account_id} set_status with message ${message}`) this.records[account_id] = message } - @view + @view({}) get_status({ account_id }) { near.log(`get_status for account_id ${account_id}`) return this.records[account_id] || null diff --git a/lib/api.d.ts b/lib/api.d.ts index 951d64150..842d6a248 100644 --- a/lib/api.d.ts +++ b/lib/api.d.ts @@ -4,13 +4,13 @@ export declare function log(...params: any[]): void; export declare function signerAccountId(): string; export declare function signerAccountPk(): Bytes; export declare function predecessorAccountId(): string; -export declare function blockIndex(): BigInt; -export declare function blockHeight(): BigInt; -export declare function blockTimestamp(): BigInt; -export declare function epochHeight(): BigInt; -export declare function attachedDeposit(): BigInt; -export declare function prepaidGas(): BigInt; -export declare function usedGas(): BigInt; +export declare function blockIndex(): bigint; +export declare function blockHeight(): bigint; +export declare function blockTimestamp(): bigint; +export declare function epochHeight(): bigint; +export declare function attachedDeposit(): bigint; +export declare function prepaidGas(): bigint; +export declare function usedGas(): bigint; export declare function randomSeed(): Bytes; export declare function sha256(value: Bytes): Bytes; export declare function keccak256(value: Bytes): Bytes; @@ -23,35 +23,35 @@ export declare function logUtf16(msg: string): void; export declare function storageRead(key: Bytes): Bytes | null; export declare function storageHasKey(key: Bytes): boolean; export declare function validatorStake(accountId: string): any; -export declare function validatorTotalStake(): BigInt; +export declare function validatorTotalStake(): bigint; export declare function altBn128G1Multiexp(value: Bytes): Bytes; export declare function altBn128G1Sum(value: Bytes): Bytes; export declare function altBn128PairingCheck(value: Bytes): boolean; export declare function storageGetEvicted(): Bytes; export declare function currentAccountId(): string; export declare function input(): Bytes; -export declare function storageUsage(): BigInt; -export declare function accountBalance(): BigInt; -export declare function accountLockedBalance(): BigInt; +export declare function storageUsage(): bigint; +export declare function accountBalance(): bigint; +export declare function accountLockedBalance(): bigint; export declare function valueReturn(value: Bytes): void; -export declare function promiseCreate(accountId: string, methodName: string, args: Bytes, amount: number | BigInt, gas: number | BigInt): BigInt; -export declare function promiseThen(promiseIndex: number | BigInt, accountId: string, methodName: string, args: Bytes, amount: number | BigInt, gas: number | BigInt): any; -export declare function promiseAnd(...promiseIndex: number[] | BigInt[]): BigInt; -export declare function promiseBatchCreate(accountId: string): BigInt; -export declare function promiseBatchThen(promiseIndex: number | BigInt, accountId: string): BigInt; -export declare function promiseBatchActionCreateAccount(promiseIndex: number | BigInt): void; -export declare function promiseBatchActionDeployContract(promiseIndex: number | BigInt, code: Bytes): void; -export declare function promiseBatchActionFunctionCall(promiseIndex: number | BigInt, methodName: string, args: Bytes, amount: number | BigInt, gas: number | BigInt): void; -export declare function promiseBatchActionTransfer(promiseIndex: number | BigInt, amount: number | BigInt): void; -export declare function promiseBatchActionStake(promiseIndex: number | BigInt, amount: number | BigInt, publicKey: Bytes): void; -export declare function promiseBatchActionAddKeyWithFullAccess(promiseIndex: number | BigInt, publicKey: Bytes, nonce: number | BigInt): void; -export declare function promiseBatchActionAddKeyWithFunctionCall(promiseIndex: number | BigInt, publicKey: Bytes, nonce: number | BigInt, allowance: number | BigInt, receiverId: string, methodNames: string): void; -export declare function promiseBatchActionDeleteKey(promiseIndex: number | BigInt, publicKey: Bytes): void; -export declare function promiseBatchActionDeleteAccount(promiseIndex: number | BigInt, beneficiaryId: string): void; -export declare function promiseBatchActionFunctionCallWeight(promiseIndex: number | BigInt, methodName: string, args: Bytes, amount: number | BigInt, gas: number | BigInt, weight: number | BigInt): void; -export declare function promiseResultsCount(): BigInt; -export declare function promiseResult(resultIdx: number | BigInt): Bytes | PromiseResult.NotReady | PromiseResult.Failed; -export declare function promiseReturn(promiseIdx: number | BigInt): void; +export declare function promiseCreate(accountId: string, methodName: string, args: Bytes, amount: number | bigint, gas: number | bigint): bigint; +export declare function promiseThen(promiseIndex: number | bigint, accountId: string, methodName: string, args: Bytes, amount: number | bigint, gas: number | bigint): any; +export declare function promiseAnd(...promiseIndex: number[] | bigint[]): bigint; +export declare function promiseBatchCreate(accountId: string): bigint; +export declare function promiseBatchThen(promiseIndex: number | bigint, accountId: string): bigint; +export declare function promiseBatchActionCreateAccount(promiseIndex: number | bigint): void; +export declare function promiseBatchActionDeployContract(promiseIndex: number | bigint, code: Bytes): void; +export declare function promiseBatchActionFunctionCall(promiseIndex: number | bigint, methodName: string, args: Bytes, amount: number | bigint, gas: number | bigint): void; +export declare function promiseBatchActionTransfer(promiseIndex: number | bigint, amount: number | bigint): void; +export declare function promiseBatchActionStake(promiseIndex: number | bigint, amount: number | bigint, publicKey: Bytes): void; +export declare function promiseBatchActionAddKeyWithFullAccess(promiseIndex: number | bigint, publicKey: Bytes, nonce: number | bigint): void; +export declare function promiseBatchActionAddKeyWithFunctionCall(promiseIndex: number | bigint, publicKey: Bytes, nonce: number | bigint, allowance: number | bigint, receiverId: string, methodNames: string): void; +export declare function promiseBatchActionDeleteKey(promiseIndex: number | bigint, publicKey: Bytes): void; +export declare function promiseBatchActionDeleteAccount(promiseIndex: number | bigint, beneficiaryId: string): void; +export declare function promiseBatchActionFunctionCallWeight(promiseIndex: number | bigint, methodName: string, args: Bytes, amount: number | bigint, gas: number | bigint, weight: number | bigint): void; +export declare function promiseResultsCount(): bigint; +export declare function promiseResult(resultIdx: number | bigint): Bytes | PromiseResult.NotReady | PromiseResult.Failed; +export declare function promiseReturn(promiseIdx: number | bigint): void; export declare function storageWrite(key: Bytes, value: Bytes): boolean; export declare function storageRemove(key: Bytes): boolean; -export declare function storageByteCost(): BigInt; +export declare function storageByteCost(): bigint; diff --git a/lib/api.js b/lib/api.js index a1ff7bb61..3ddde250c 100644 --- a/lib/api.js +++ b/lib/api.js @@ -140,6 +140,7 @@ export function accountLockedBalance() { return env.account_locked_balance(); } export function valueReturn(value) { + log('valueReturn'); env.value_return(value); } export function promiseCreate(accountId, methodName, args, amount, gas) { @@ -204,6 +205,7 @@ export function promiseResult(resultIdx) { } } export function promiseReturn(promiseIdx) { + log('promiseReturn'); env.promise_return(promiseIdx); } export function storageWrite(key, value) { diff --git a/lib/build-tools/near-bindgen-exporter.js b/lib/build-tools/near-bindgen-exporter.js index 4dfc8be74..4a201dd08 100644 --- a/lib/build-tools/near-bindgen-exporter.js +++ b/lib/build-tools/near-bindgen-exporter.js @@ -9,15 +9,15 @@ export default function () { let contractMethods = {}; for (let child of classNode.body.body) { if (child.type == 'ClassMethod' && child.kind == 'method' && child.decorators) { - if (child.decorators[0].expression.name == 'call') { + if (child.decorators[0].expression.callee.name == 'call') { let callMethod = child.key.name; contractMethods[callMethod] = 'call'; } - else if (child.decorators[0].expression.name == 'view') { + else if (child.decorators[0].expression.callee.name == 'view') { let viewMethod = child.key.name; contractMethods[viewMethod] = 'view'; } - else if (child.decorators[0].expression.name == 'initialize') { + else if (child.decorators[0].expression.callee.name == 'initialize') { let initMethod = child.key.name; contractMethods[initMethod] = 'initialize'; } @@ -48,8 +48,12 @@ export default function () { // Counter._saveToStorage(_contract); t.expressionStatement(t.callExpression(t.memberExpression(classId, t.identifier('_saveToStorage')), [t.identifier('_contract')])) : t.emptyStatement(), - // if (_result !== undefined) near.valueReturn(_contract._serialize(result)); - t.ifStatement(t.binaryExpression('!==', t.identifier('_result'), t.identifier('undefined')), t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('env'), t.identifier('value_return')), [t.callExpression(t.memberExpression(classId, t.identifier('_serialize')), [t.identifier('_result')])]))), + // if (_result !== undefined) + // if (_result && _result.constructor && _result.constructor.name === 'NearPromise') + // _result.onReturn(); + // else + // near.valueReturn(_contract._serialize(result)); + t.ifStatement(t.binaryExpression('!==', t.identifier('_result'), t.identifier('undefined')), t.ifStatement(t.logicalExpression('&&', t.logicalExpression('&&', t.identifier('_result'), t.memberExpression(t.identifier('_result'), t.identifier('constructor'))), t.binaryExpression('===', t.memberExpression(t.memberExpression(t.identifier('_result'), t.identifier('constructor')), t.identifier('name')), t.stringLiteral('NearPromise'))), t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('_result'), t.identifier('onReturn')), [])), t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('env'), t.identifier('value_return')), [t.callExpression(t.memberExpression(classId, t.identifier('_serialize')), [t.identifier('_result')])])))) ])))); console.log(`Babel ${method} method export done`); } diff --git a/lib/index.d.ts b/lib/index.d.ts index 972642e71..3643c08fe 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -2,4 +2,5 @@ import { call, view, initialize, NearBindgen } from "./near-bindgen"; import * as near from "./api"; import { LookupMap, Vector, LookupSet, UnorderedMap, UnorderedSet } from "./collections"; import { bytes, Bytes, assert } from "./utils"; -export { call, view, initialize, NearBindgen, near, LookupMap, Vector, LookupSet, UnorderedMap, UnorderedSet, bytes, Bytes, assert, }; +import { NearPromise, PromiseOrValue } from "./promise"; +export { call, view, initialize, NearBindgen, near, LookupMap, Vector, LookupSet, UnorderedMap, UnorderedSet, bytes, Bytes, assert, NearPromise, PromiseOrValue }; diff --git a/lib/index.js b/lib/index.js index c5861a788..836c2647a 100644 --- a/lib/index.js +++ b/lib/index.js @@ -2,4 +2,5 @@ import { call, view, initialize, NearBindgen } from "./near-bindgen"; import * as near from "./api"; import { LookupMap, Vector, LookupSet, UnorderedMap, UnorderedSet, } from "./collections"; import { bytes, assert } from "./utils"; -export { call, view, initialize, NearBindgen, near, LookupMap, Vector, LookupSet, UnorderedMap, UnorderedSet, bytes, assert, }; +import { NearPromise } from "./promise"; +export { call, view, initialize, NearBindgen, near, LookupMap, Vector, LookupSet, UnorderedMap, UnorderedSet, bytes, assert, NearPromise }; diff --git a/lib/near-bindgen.d.ts b/lib/near-bindgen.d.ts index d3efdeda1..67608c49d 100644 --- a/lib/near-bindgen.d.ts +++ b/lib/near-bindgen.d.ts @@ -1,6 +1,9 @@ -export declare function initialize(target: Object, key: string | symbol, descriptor: TypedPropertyDescriptor): void; -export declare function call(target: Object, key: string | symbol, descriptor: TypedPropertyDescriptor): void; -export declare function view(target: Object, key: string | symbol, descriptor: TypedPropertyDescriptor): void; +export declare function initialize({}: {}): (target: Object, key: string | symbol, descriptor: TypedPropertyDescriptor) => void; +export declare function call({ privateFunction, payableFunction }: { + privateFunction?: boolean; + payableFunction?: boolean; +}): (target: Object, key: string | symbol, descriptor: TypedPropertyDescriptor) => void; +export declare function view({}: {}): (target: Object, key: string | symbol, descriptor: TypedPropertyDescriptor) => void; export declare function NearBindgen({ requireInit }: { requireInit?: boolean; }): {}>(target: T) => { diff --git a/lib/near-bindgen.js b/lib/near-bindgen.js index 07a6c7e11..d266db78c 100644 --- a/lib/near-bindgen.js +++ b/lib/near-bindgen.js @@ -1,9 +1,25 @@ import * as near from "./api"; -export function initialize(target, key, descriptor) { +export function initialize({}) { + return function (target, key, descriptor) { + }; } -export function call(target, key, descriptor) { +export function call({ privateFunction = false, payableFunction = false }) { + return function (target, key, descriptor) { + const originalMethod = descriptor.value; + descriptor.value = function (...args) { + if (privateFunction && near.predecessorAccountId() !== near.currentAccountId()) { + throw Error("Function is private"); + } + if (!payableFunction && near.attachedDeposit() > BigInt(0)) { + throw Error("Function is not payable"); + } + return originalMethod.apply(this, args); + }; + }; } -export function view(target, key, descriptor) { +export function view({}) { + return function (target, key, descriptor) { + }; } export function NearBindgen({ requireInit = false }) { return (target) => { diff --git a/lib/promise.d.ts b/lib/promise.d.ts new file mode 100644 index 000000000..936ac4532 --- /dev/null +++ b/lib/promise.d.ts @@ -0,0 +1,109 @@ +import { Bytes } from "./utils"; +import { Balance, PublicKey, AccountId, Gas, GasWeight } from "./types"; +import { Nonce } from "./types/primitives"; +export declare abstract class PromiseAction { + abstract add(promise_index: number | bigint): void; +} +export declare class CreateAccount extends PromiseAction { + add(promise_index: number | bigint): void; +} +export declare class DeployContract extends PromiseAction { + code: Bytes; + constructor(code: Bytes); + add(promise_index: number | bigint): void; +} +export declare class FunctionCall extends PromiseAction { + function_name: string; + args: Bytes; + amount: Balance; + gas: Gas; + constructor(function_name: string, args: Bytes, amount: Balance, gas: Gas); + add(promise_index: number | bigint): void; +} +export declare class FunctionCallWeight extends PromiseAction { + function_name: string; + args: Bytes; + amount: Balance; + gas: Gas; + weight: GasWeight; + constructor(function_name: string, args: Bytes, amount: Balance, gas: Gas, weight: GasWeight); + add(promise_index: number | bigint): void; +} +export declare class Transfer extends PromiseAction { + amount: Balance; + constructor(amount: Balance); + add(promise_index: number | bigint): void; +} +export declare class Stake extends PromiseAction { + amount: Balance; + public_key: PublicKey; + constructor(amount: Balance, public_key: PublicKey); + add(promise_index: number | bigint): void; +} +export declare class AddFullAccessKey extends PromiseAction { + public_key: PublicKey; + nonce: Nonce; + constructor(public_key: PublicKey, nonce: Nonce); + add(promise_index: number | bigint): void; +} +export declare class AddAccessKey extends PromiseAction { + public_key: PublicKey; + allowance: Balance; + receiver_id: AccountId; + function_names: string; + nonce: Nonce; + constructor(public_key: PublicKey, allowance: Balance, receiver_id: AccountId, function_names: string, nonce: Nonce); + add(promise_index: number | bigint): void; +} +export declare class DeleteKey extends PromiseAction { + public_key: PublicKey; + constructor(public_key: PublicKey); + add(promise_index: number | bigint): void; +} +export declare class DeleteAccount extends PromiseAction { + beneficiary_id: AccountId; + constructor(beneficiary_id: AccountId); + add(promise_index: number | bigint): void; +} +declare class PromiseSingle { + account_id: AccountId; + actions: PromiseAction[]; + after: NearPromise | null; + promise_index: number | bigint | null; + constructor(account_id: AccountId, actions: PromiseAction[], after: NearPromise | null, promise_index: number | bigint | null); + constructRecursively(): number | bigint; +} +export declare class PromiseJoint { + promise_a: NearPromise; + promise_b: NearPromise; + promise_index: number | bigint | null; + constructor(promise_a: NearPromise, promise_b: NearPromise, promise_index: number | bigint | null); + constructRecursively(): number | bigint; +} +declare type PromiseSubtype = PromiseSingle | PromiseJoint; +export declare class NearPromise { + private subtype; + private should_return; + constructor(subtype: PromiseSubtype, should_return: boolean); + static new(account_id: AccountId): NearPromise; + private add_action; + createAccount(): NearPromise; + deployContract(code: Bytes): NearPromise; + functionCall(function_name: string, args: Bytes, amount: Balance, gas: Gas): NearPromise; + functionCallWeight(function_name: string, args: Bytes, amount: Balance, gas: Gas, weight: GasWeight): NearPromise; + transfer(amount: Balance): NearPromise; + stake(amount: Balance, public_key: PublicKey): NearPromise; + addFullAccessKey(public_key: PublicKey): NearPromise; + addFullAccessKeyWithNonce(public_key: PublicKey, nonce: Nonce): NearPromise; + addAccessKey(public_key: PublicKey, allowance: Balance, receiver_id: AccountId, method_names: string): NearPromise; + addAccessKeyWithNonce(public_key: PublicKey, allowance: Balance, receiver_id: AccountId, method_names: string, nonce: Nonce): NearPromise; + deleteKey(public_key: PublicKey): NearPromise; + deleteAccount(beneficiary_id: AccountId): NearPromise; + and(other: NearPromise): NearPromise; + then(other: NearPromise): NearPromise; + asReturn(): NearPromise; + constructRecursively(): number | bigint; + onReturn(): void; +} +export declare type PromiseOrValue = NearPromise | T; +export {}; diff --git a/lib/promise.js b/lib/promise.js new file mode 100644 index 000000000..f71c0010f --- /dev/null +++ b/lib/promise.js @@ -0,0 +1,230 @@ +import * as near from "./api"; +export class PromiseAction { +} +export class CreateAccount extends PromiseAction { + add(promise_index) { + near.promiseBatchActionCreateAccount(promise_index); + } +} +export class DeployContract extends PromiseAction { + constructor(code) { + super(); + this.code = code; + } + add(promise_index) { + near.promiseBatchActionDeployContract(promise_index, this.code); + } +} +export class FunctionCall extends PromiseAction { + constructor(function_name, args, amount, gas) { + super(); + this.function_name = function_name; + this.args = args; + this.amount = amount; + this.gas = gas; + } + add(promise_index) { + near.promiseBatchActionFunctionCall(promise_index, this.function_name, this.args, this.amount, this.gas); + } +} +export class FunctionCallWeight extends PromiseAction { + constructor(function_name, args, amount, gas, weight) { + super(); + this.function_name = function_name; + this.args = args; + this.amount = amount; + this.gas = gas; + this.weight = weight; + } + add(promise_index) { + near.promiseBatchActionFunctionCallWeight(promise_index, this.function_name, this.args, this.amount, this.gas, this.weight); + } +} +export class Transfer extends PromiseAction { + constructor(amount) { + super(); + this.amount = amount; + } + add(promise_index) { + near.promiseBatchActionTransfer(promise_index, this.amount); + } +} +export class Stake extends PromiseAction { + constructor(amount, public_key) { + super(); + this.amount = amount; + this.public_key = public_key; + } + add(promise_index) { + near.promiseBatchActionStake(promise_index, this.amount, this.public_key.data); + } +} +export class AddFullAccessKey extends PromiseAction { + constructor(public_key, nonce) { + super(); + this.public_key = public_key; + this.nonce = nonce; + } + add(promise_index) { + near.promiseBatchActionAddKeyWithFullAccess(promise_index, this.public_key.data, this.nonce); + } +} +export class AddAccessKey extends PromiseAction { + constructor(public_key, allowance, receiver_id, function_names, nonce) { + super(); + this.public_key = public_key; + this.allowance = allowance; + this.receiver_id = receiver_id; + this.function_names = function_names; + this.nonce = nonce; + } + add(promise_index) { + near.promiseBatchActionAddKeyWithFunctionCall(promise_index, this.public_key.data, this.nonce, this.allowance, this.receiver_id, this.function_names); + } +} +export class DeleteKey extends PromiseAction { + constructor(public_key) { + super(); + this.public_key = public_key; + } + add(promise_index) { + near.promiseBatchActionDeleteKey(promise_index, this.public_key.data); + } +} +export class DeleteAccount extends PromiseAction { + constructor(beneficiary_id) { + super(); + this.beneficiary_id = beneficiary_id; + } + add(promise_index) { + near.promiseBatchActionDeleteAccount(promise_index, this.beneficiary_id); + } +} +class PromiseSingle { + constructor(account_id, actions, after, promise_index) { + this.account_id = account_id; + this.actions = actions; + this.after = after; + this.promise_index = promise_index; + } + constructRecursively() { + if (this.promise_index !== null) { + return this.promise_index; + } + let promise_index; + if (this.after) { + promise_index = near.promiseBatchThen(this.after.constructRecursively(), this.account_id); + } + else { + promise_index = near.promiseBatchCreate(this.account_id); + } + for (let action of this.actions) { + action.add(promise_index); + } + this.promise_index = promise_index; + return promise_index; + } +} +export class PromiseJoint { + constructor(promise_a, promise_b, promise_index) { + this.promise_a = promise_a; + this.promise_b = promise_b; + this.promise_index = promise_index; + } + constructRecursively() { + if (this.promise_index !== null) { + return this.promise_index; + } + let res = near.promiseAnd(BigInt(this.promise_a.constructRecursively()), BigInt(this.promise_b.constructRecursively())); + this.promise_index = res; + return res; + } +} +export class NearPromise { + constructor(subtype, should_return) { + this.subtype = subtype; + this.should_return = should_return; + } + static new(account_id) { + let subtype = new PromiseSingle(account_id, [], null, null); + let ret = new NearPromise(subtype, false); + return ret; + } + add_action(action) { + if (this.subtype instanceof PromiseJoint) { + throw new Error("Cannot add action to a joint promise."); + } + else { + this.subtype.actions.push(action); + } + return this; + } + createAccount() { + return this.add_action(new CreateAccount()); + } + deployContract(code) { + return this.add_action(new DeployContract(code)); + } + functionCall(function_name, args, amount, gas) { + return this.add_action(new FunctionCall(function_name, args, amount, gas)); + } + functionCallWeight(function_name, args, amount, gas, weight) { + return this.add_action(new FunctionCallWeight(function_name, args, amount, gas, weight)); + } + transfer(amount) { + return this.add_action(new Transfer(amount)); + } + stake(amount, public_key) { + return this.add_action(new Stake(amount, public_key)); + } + addFullAccessKey(public_key) { + return this.addFullAccessKeyWithNonce(public_key, 0n); + } + addFullAccessKeyWithNonce(public_key, nonce) { + return this.add_action(new AddFullAccessKey(public_key, nonce)); + } + addAccessKey(public_key, allowance, receiver_id, method_names) { + return this.addAccessKeyWithNonce(public_key, allowance, receiver_id, method_names, 0n); + } + addAccessKeyWithNonce(public_key, allowance, receiver_id, method_names, nonce) { + return this.add_action(new AddAccessKey(public_key, allowance, receiver_id, method_names, nonce)); + } + deleteKey(public_key) { + return this.add_action(new DeleteKey(public_key)); + } + deleteAccount(beneficiary_id) { + return this.add_action(new DeleteAccount(beneficiary_id)); + } + and(other) { + let subtype = new PromiseJoint(this, other, null); + let ret = new NearPromise(subtype, false); + return ret; + } + then(other) { + if (other.subtype instanceof PromiseSingle) { + if (other.subtype.after !== null) { + throw new Error("Cannot callback promise which is already scheduled after another"); + } + other.subtype.after = this; + } + else { + throw new Error("Cannot callback joint promise."); + } + return other; + } + asReturn() { + this.should_return = true; + return this; + } + constructRecursively() { + let res = this.subtype.constructRecursively(); + if (this.should_return) { + near.promiseReturn(res); + } + return res; + } + // Called by NearBindgen, when return object is a NearPromise instance. + onReturn() { + this.asReturn().constructRecursively(); + } +} diff --git a/lib/types/index.d.ts b/lib/types/index.d.ts index a5234793e..82da4d59f 100644 --- a/lib/types/index.d.ts +++ b/lib/types/index.d.ts @@ -4,3 +4,6 @@ import { PromiseResult, PromiseError, PromiseIndex, ReceiptIndex, IteratorIndex import { Gas, ONE_TERA_GAS } from "./gas"; import { PublicKey, CurveType, curveTypeFromStr, ParsePublicKeyError, InvalidLengthError, Base58Error, UnknownCurve } from "./public_key"; export { AccountId, BlockHeight, EpochHeight, Balance, StorageUsage, PromiseResult, PromiseError, PromiseIndex, ReceiptIndex, IteratorIndex, Gas, ONE_TERA_GAS, PublicKey, CurveType, curveTypeFromStr, ParsePublicKeyError, InvalidLengthError, Base58Error, UnknownCurve }; +export declare type GasWeight = bigint; +export declare const ONE_YOCTO: Balance; +export declare const ONE_NEAR: Balance; diff --git a/lib/types/index.js b/lib/types/index.js index 8fbfa22dd..4efb790e0 100644 --- a/lib/types/index.js +++ b/lib/types/index.js @@ -2,3 +2,5 @@ import { PromiseResult, PromiseError } from "./vm_types"; import { ONE_TERA_GAS } from "./gas"; import { PublicKey, CurveType, curveTypeFromStr, ParsePublicKeyError, InvalidLengthError, Base58Error, UnknownCurve } from "./public_key"; export { PromiseResult, PromiseError, ONE_TERA_GAS, PublicKey, CurveType, curveTypeFromStr, ParsePublicKeyError, InvalidLengthError, Base58Error, UnknownCurve }; +export const ONE_YOCTO = 1n; +export const ONE_NEAR = 1000000000000000000000000n; diff --git a/lib/types/primitives.d.ts b/lib/types/primitives.d.ts index b98e51910..6cd71162d 100644 --- a/lib/types/primitives.d.ts +++ b/lib/types/primitives.d.ts @@ -2,3 +2,4 @@ export declare type StorageUsage = bigint; export declare type BlockHeight = bigint; export declare type EpochHeight = bigint; export declare type Balance = bigint; +export declare type Nonce = bigint; diff --git a/package.json b/package.json index 6628130c3..4d9e127ad 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,6 @@ "@rollup/plugin-commonjs": "^21.0.1", "@rollup/plugin-node-resolve": "^13.1.1", "@scure/base": "^1.1.1", - "bs58": "^5.0.0", "rollup": "^2.61.1", "rollup-plugin-sourcemaps": "^0.6.3", "yargs": "^17.5.1" diff --git a/src/api.ts b/src/api.ts index ce020b3e9..074868033 100644 --- a/src/api.ts +++ b/src/api.ts @@ -35,31 +35,31 @@ export function predecessorAccountId(): string { return env.read_register(0); } -export function blockIndex(): BigInt { +export function blockIndex(): bigint { return env.block_index(); } -export function blockHeight(): BigInt { +export function blockHeight(): bigint { return blockIndex(); } -export function blockTimestamp(): BigInt { +export function blockTimestamp(): bigint { return env.block_timestamp(); } -export function epochHeight(): BigInt { +export function epochHeight(): bigint { return env.epoch_height(); } -export function attachedDeposit(): BigInt { +export function attachedDeposit(): bigint { return env.attached_deposit(); } -export function prepaidGas(): BigInt { +export function prepaidGas(): bigint { return env.prepaid_gas(); } -export function usedGas(): BigInt { +export function usedGas(): bigint { return env.used_gas(); } @@ -137,7 +137,7 @@ export function validatorStake(accountId: string) { return env.validator_stake(accountId); } -export function validatorTotalStake(): BigInt { +export function validatorTotalStake(): bigint { return env.validator_total_stake(); } @@ -174,15 +174,15 @@ export function input(): Bytes { return env.read_register(0); } -export function storageUsage(): BigInt { +export function storageUsage(): bigint { return env.storage_usage(); } -export function accountBalance(): BigInt { +export function accountBalance(): bigint { return env.account_balance(); } -export function accountLockedBalance(): BigInt { +export function accountLockedBalance(): bigint { return env.account_locked_balance(); } @@ -194,19 +194,19 @@ export function promiseCreate( accountId: string, methodName: string, args: Bytes, - amount: number | BigInt, - gas: number | BigInt -): BigInt { + amount: number | bigint, + gas: number | bigint +): bigint { return env.promise_create(accountId, methodName, args, amount, gas); } export function promiseThen( - promiseIndex: number | BigInt, + promiseIndex: number | bigint, accountId: string, methodName: string, args: Bytes, - amount: number | BigInt, - gas: number | BigInt + amount: number | bigint, + gas: number | bigint ) { return env.promise_then( promiseIndex, @@ -218,38 +218,38 @@ export function promiseThen( ); } -export function promiseAnd(...promiseIndex: number[] | BigInt[]): BigInt { +export function promiseAnd(...promiseIndex: number[] | bigint[]): bigint { return env.promise_and(...promiseIndex); } -export function promiseBatchCreate(accountId: string): BigInt { +export function promiseBatchCreate(accountId: string): bigint { return env.promise_batch_create(accountId); } export function promiseBatchThen( - promiseIndex: number | BigInt, + promiseIndex: number | bigint, accountId: string -): BigInt { +): bigint { return env.promise_batch_then(promiseIndex, accountId); } -export function promiseBatchActionCreateAccount(promiseIndex: number | BigInt) { +export function promiseBatchActionCreateAccount(promiseIndex: number | bigint) { env.promise_batch_action_create_account(promiseIndex); } export function promiseBatchActionDeployContract( - promiseIndex: number | BigInt, + promiseIndex: number | bigint, code: Bytes ) { env.promise_batch_action_deploy_contract(promiseIndex, code); } export function promiseBatchActionFunctionCall( - promiseIndex: number | BigInt, + promiseIndex: number | bigint, methodName: string, args: Bytes, - amount: number | BigInt, - gas: number | BigInt + amount: number | bigint, + gas: number | bigint ) { env.promise_batch_action_function_call( promiseIndex, @@ -261,24 +261,24 @@ export function promiseBatchActionFunctionCall( } export function promiseBatchActionTransfer( - promiseIndex: number | BigInt, - amount: number | BigInt + promiseIndex: number | bigint, + amount: number | bigint ) { env.promise_batch_action_transfer(promiseIndex, amount); } export function promiseBatchActionStake( - promiseIndex: number | BigInt, - amount: number | BigInt, + promiseIndex: number | bigint, + amount: number | bigint, publicKey: Bytes ) { env.promise_batch_action_stake(promiseIndex, amount, publicKey); } export function promiseBatchActionAddKeyWithFullAccess( - promiseIndex: number | BigInt, + promiseIndex: number | bigint, publicKey: Bytes, - nonce: number | BigInt + nonce: number | bigint ) { env.promise_batch_action_add_key_with_full_access( promiseIndex, @@ -288,10 +288,10 @@ export function promiseBatchActionAddKeyWithFullAccess( } export function promiseBatchActionAddKeyWithFunctionCall( - promiseIndex: number | BigInt, + promiseIndex: number | bigint, publicKey: Bytes, - nonce: number | BigInt, - allowance: number | BigInt, + nonce: number | bigint, + allowance: number | bigint, receiverId: string, methodNames: string ) { @@ -306,26 +306,26 @@ export function promiseBatchActionAddKeyWithFunctionCall( } export function promiseBatchActionDeleteKey( - promiseIndex: number | BigInt, + promiseIndex: number | bigint, publicKey: Bytes ) { env.promise_batch_action_delete_key(promiseIndex, publicKey); } export function promiseBatchActionDeleteAccount( - promiseIndex: number | BigInt, + promiseIndex: number | bigint, beneficiaryId: string ) { env.promise_batch_action_delete_account(promiseIndex, beneficiaryId); } export function promiseBatchActionFunctionCallWeight( - promiseIndex: number | BigInt, + promiseIndex: number | bigint, methodName: string, args: Bytes, - amount: number | BigInt, - gas: number | BigInt, - weight: number | BigInt, + amount: number | bigint, + gas: number | bigint, + weight: number | bigint, ) { env.promise_batch_action_function_call_weight( promiseIndex, @@ -337,12 +337,12 @@ export function promiseBatchActionFunctionCallWeight( ); } -export function promiseResultsCount(): BigInt { +export function promiseResultsCount(): bigint { return env.promise_results_count(); } export function promiseResult( - resultIdx: number | BigInt + resultIdx: number | bigint ): Bytes | PromiseResult.NotReady | PromiseResult.Failed { let status: PromiseResult = env.promise_result(resultIdx, 0); if (status == PromiseResult.Successful) { @@ -357,7 +357,7 @@ export function promiseResult( } } -export function promiseReturn(promiseIdx: number | BigInt) { +export function promiseReturn(promiseIdx: number | bigint) { env.promise_return(promiseIdx); } @@ -377,6 +377,6 @@ export function storageRemove(key: Bytes): boolean { return false; } -export function storageByteCost(): BigInt { +export function storageByteCost(): bigint { return 10_000_000_000_000_000_000n; } diff --git a/src/build-tools/near-bindgen-exporter.js b/src/build-tools/near-bindgen-exporter.js index 8cd2780fa..f4e4335be 100644 --- a/src/build-tools/near-bindgen-exporter.js +++ b/src/build-tools/near-bindgen-exporter.js @@ -10,15 +10,15 @@ export default function () { let contractMethods = {}; for (let child of classNode.body.body) { if (child.type == 'ClassMethod' && child.kind == 'method' && child.decorators) { - if (child.decorators[0].expression.name == 'call') { + if (child.decorators[0].expression.callee.name == 'call') { let callMethod = child.key.name; contractMethods[callMethod] = 'call'; } - else if (child.decorators[0].expression.name == 'view') { + else if (child.decorators[0].expression.callee.name == 'view') { let viewMethod = child.key.name; contractMethods[viewMethod] = 'view'; } - else if (child.decorators[0].expression.name == 'initialize') { + else if (child.decorators[0].expression.callee.name == 'initialize') { let initMethod = child.key.name; contractMethods[initMethod] = 'initialize'; } @@ -50,9 +50,16 @@ export default function () { // Counter._saveToStorage(_contract); t.expressionStatement(t.callExpression(t.memberExpression(classId, t.identifier('_saveToStorage')), [t.identifier('_contract')])) : t.emptyStatement(), - // if (_result !== undefined) near.valueReturn(_contract._serialize(result)); - t.ifStatement(t.binaryExpression('!==', t.identifier('_result'), t.identifier('undefined')), t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('env'), t.identifier('value_return')), [t.callExpression(t.memberExpression(classId, t.identifier('_serialize')), [t.identifier('_result')])]))), - ])))); + // if (_result !== undefined) + // if (_result && _result.constructor && _result.constructor.name === 'NearPromise') + // _result.onReturn(); + // else + // near.valueReturn(_contract._serialize(result)); + t.ifStatement(t.binaryExpression('!==', t.identifier('_result'), t.identifier('undefined')), + t.ifStatement(t.logicalExpression('&&', t.logicalExpression('&&', t.identifier('_result'), t.memberExpression(t.identifier('_result'), t.identifier('constructor'))), + t.binaryExpression('===', t.memberExpression(t.memberExpression(t.identifier('_result'), t.identifier('constructor')), t.identifier('name')), t.stringLiteral('NearPromise'))), + t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('_result'), t.identifier('onReturn')), [])), + t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('env'), t.identifier('value_return')), [t.callExpression(t.memberExpression(classId, t.identifier('_serialize')), [t.identifier('_result')])]))))])))); console.log(`Babel ${method} method export done`); } } diff --git a/src/index.ts b/src/index.ts index 66b3ad332..2eef8c0b4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -16,6 +16,8 @@ import { import { bytes, Bytes, assert } from "./utils"; +import { NearPromise, PromiseOrValue } from "./promise"; + export { call, view, @@ -30,4 +32,6 @@ export { bytes, Bytes, assert, + NearPromise, + PromiseOrValue }; diff --git a/src/near-bindgen.ts b/src/near-bindgen.ts index 9cf0d0ec6..9b9d40f87 100644 --- a/src/near-bindgen.ts +++ b/src/near-bindgen.ts @@ -1,12 +1,28 @@ import * as near from "./api"; -export function initialize(target: Object, key: string | symbol, descriptor: TypedPropertyDescriptor): void { +export function initialize({ }) { + return function (target: Object, key: string | symbol, descriptor: TypedPropertyDescriptor): void { + } } -export function call(target: Object, key: string | symbol, descriptor: TypedPropertyDescriptor): void { +export function call({ privateFunction = false, payableFunction = false }: { privateFunction?: boolean, payableFunction?: boolean }) { + return function (target: Object, key: string | symbol, descriptor: TypedPropertyDescriptor): void { + const originalMethod = descriptor.value; + descriptor.value = function (...args: any[]) { + if (privateFunction && near.predecessorAccountId() !== near.currentAccountId()) { + throw Error("Function is private"); + } + if (!payableFunction && near.attachedDeposit() > BigInt(0)) { + throw Error("Function is not payable"); + } + return originalMethod.apply(this, args); + } + } } -export function view(target: Object, key: string | symbol, descriptor: TypedPropertyDescriptor): void { +export function view({ }) { + return function (target: Object, key: string | symbol, descriptor: TypedPropertyDescriptor): void { + } } export function NearBindgen({ requireInit = false }: { requireInit?: boolean }) { diff --git a/src/promise.ts b/src/promise.ts index f195811d3..7de4799af 100644 --- a/src/promise.ts +++ b/src/promise.ts @@ -1,22 +1,228 @@ -import { Bytes } from ".."; -import { Balance } from "./types"; +import { Bytes } from "./utils"; +import * as near from "./api"; +import { Balance, PublicKey, AccountId, Gas, GasWeight } from "./types"; +import { Nonce } from "./types/primitives"; -export class CreateAccount {} -export class DeployContract { - constructor(public code: Bytes) {} +export abstract class PromiseAction { + abstract add(promise_index: number | bigint): void; } -export class FunctionCall { - constructor(public function_name: string, public args: Bytes, public amount: Balance) {} + +export class CreateAccount extends PromiseAction { + add(promise_index: number | bigint) { + near.promiseBatchActionCreateAccount(promise_index); + } +} + +export class DeployContract extends PromiseAction { + constructor(public code: Bytes) { super() } + + add(promise_index: number | bigint) { + near.promiseBatchActionDeployContract(promise_index, this.code); + } +} + +export class FunctionCall extends PromiseAction{ + constructor(public function_name: string, public args: Bytes, public amount: Balance, public gas: Gas) { super() } + + add(promise_index: number | bigint) { + near.promiseBatchActionFunctionCall(promise_index, this.function_name, this.args, this.amount, this.gas); + } +} + +export class FunctionCallWeight extends PromiseAction { + constructor(public function_name: string, public args: Bytes, public amount: Balance, public gas: Gas, public weight: GasWeight) { super() } + + add(promise_index: number | bigint) { + near.promiseBatchActionFunctionCallWeight(promise_index, this.function_name, this.args, this.amount, this.gas, this.weight); + } +} + +export class Transfer extends PromiseAction { + constructor(public amount: Balance) { super() } + + add(promise_index: number | bigint) { + near.promiseBatchActionTransfer(promise_index, this.amount); + } +} + +export class Stake extends PromiseAction { + constructor(public amount: Balance, public public_key: PublicKey) { super() } + + add(promise_index: number | bigint) { + near.promiseBatchActionStake(promise_index, this.amount, this.public_key.data); + } +} + +export class AddFullAccessKey extends PromiseAction { + constructor(public public_key: PublicKey, public nonce: Nonce) { super() } + + add(promise_index: number | bigint) { + near.promiseBatchActionAddKeyWithFullAccess(promise_index, this.public_key.data, this.nonce); + } +} + +export class AddAccessKey extends PromiseAction { + constructor(public public_key: PublicKey, public allowance: Balance, public receiver_id: AccountId, public function_names: string, public nonce: Nonce) { super() } + + add(promise_index: number | bigint) { + near.promiseBatchActionAddKeyWithFunctionCall(promise_index, this.public_key.data, this.nonce, this.allowance, this.receiver_id, this.function_names); + } +} + +export class DeleteKey extends PromiseAction { + constructor(public public_key: PublicKey) { super() } + + add(promise_index: number | bigint) { + near.promiseBatchActionDeleteKey(promise_index, this.public_key.data); + } +} + +export class DeleteAccount extends PromiseAction { + constructor(public beneficiary_id: AccountId) { super() } + + add(promise_index: number | bigint) { + near.promiseBatchActionDeleteAccount(promise_index, this.beneficiary_id); + } } -// TODO add FunctionCallWeight after add that in api.ts -export class Transfer { - constructor(public amount: Balance) {} + +class PromiseSingle { + constructor(public account_id: AccountId, public actions: PromiseAction[], public after: NearPromise | null, public promise_index: number | bigint | null) { } + + constructRecursively(): number | bigint { + if (this.promise_index !== null) { + return this.promise_index; + } + let promise_index; + if (this.after) { + promise_index = near.promiseBatchThen(this.after.constructRecursively(), this.account_id) + } else { + promise_index = near.promiseBatchCreate(this.account_id); + } + for (let action of this.actions) { + action.add(promise_index); + } + this.promise_index = promise_index; + return promise_index + } } -export class Stake { - constructor(public amount: Balance, public public_key: PublicKey) {} + +export class PromiseJoint { + constructor(public promise_a: NearPromise, public promise_b: NearPromise, public promise_index: number | bigint | null) { } + + constructRecursively(): number | bigint { + if (this.promise_index !== null) { + return this.promise_index; + } + let res = near.promiseAnd(BigInt(this.promise_a.constructRecursively()), BigInt(this.promise_b.constructRecursively())); + this.promise_index = res + return res + } } -export type PromiseAction = [string] -export type A = [string] -let a: A = ["a"] -let b: PromiseAction = ["a"] +type PromiseSubtype = PromiseSingle | PromiseJoint; + +export class NearPromise { + constructor(private subtype: PromiseSubtype, private should_return: boolean) { } + + static new(account_id: AccountId): NearPromise { + let subtype = new PromiseSingle(account_id, [], null, null); + let ret = new NearPromise(subtype, false); + return ret; + } + + private add_action(action: PromiseAction): NearPromise{ + if (this.subtype instanceof PromiseJoint) { + throw new Error("Cannot add action to a joint promise.") + } else { + this.subtype.actions.push(action); + } + return this; + } + + createAccount(): NearPromise { + return this.add_action(new CreateAccount()); + } + + deployContract(code: Bytes): NearPromise { + return this.add_action(new DeployContract(code)); + } + + functionCall(function_name: string, args: Bytes, amount: Balance, gas: Gas): NearPromise { + return this.add_action(new FunctionCall(function_name, args, amount, gas)); + } + + functionCallWeight(function_name: string, args: Bytes, amount: Balance, gas: Gas, weight: GasWeight): NearPromise { + return this.add_action(new FunctionCallWeight(function_name, args, amount, gas, weight)); + } + + transfer(amount: Balance): NearPromise { + return this.add_action(new Transfer(amount)); + } + + stake(amount: Balance, public_key: PublicKey): NearPromise { + return this.add_action(new Stake(amount, public_key)); + } + + addFullAccessKey(public_key: PublicKey): NearPromise { + return this.addFullAccessKeyWithNonce(public_key, 0n) + } + + addFullAccessKeyWithNonce(public_key: PublicKey, nonce: Nonce): NearPromise { + return this.add_action(new AddFullAccessKey(public_key, nonce)); + } + + addAccessKey(public_key: PublicKey, allowance: Balance, receiver_id: AccountId, method_names: string): NearPromise { + return this.addAccessKeyWithNonce(public_key, allowance, receiver_id, method_names, 0n) + } + + addAccessKeyWithNonce(public_key: PublicKey, allowance: Balance, receiver_id: AccountId, method_names: string, nonce: Nonce): NearPromise { + return this.add_action(new AddAccessKey(public_key, allowance, receiver_id, method_names, nonce)); + } + + deleteKey(public_key: PublicKey): NearPromise { + return this.add_action(new DeleteKey(public_key)); + } + + deleteAccount(beneficiary_id: AccountId): NearPromise { + return this.add_action(new DeleteAccount(beneficiary_id)); + } + + and(other: NearPromise): NearPromise { + let subtype = new PromiseJoint(this, other, null); + let ret = new NearPromise(subtype, false); + return ret; + } + + then(other: NearPromise): NearPromise { + if (other.subtype instanceof PromiseSingle) { + if (other.subtype.after !== null) { + throw new Error("Cannot callback promise which is already scheduled after another"); + } + other.subtype.after = this; + } else { + throw new Error("Cannot callback joint promise.") + } + return other; + } + + asReturn(): NearPromise { + this.should_return = true; + return this; + } + + constructRecursively(): number | bigint { + let res = this.subtype.constructRecursively(); + if (this.should_return) { + near.promiseReturn(res); + } + return res; + } + + // Called by NearBindgen, when return object is a NearPromise instance. + onReturn() { + this.asReturn().constructRecursively(); + } +} + +export type PromiseOrValue = NearPromise | T; + diff --git a/src/types/index.ts b/src/types/index.ts index 73601a20b..ccec78c05 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -6,3 +6,7 @@ import { PublicKey, CurveType, curveTypeFromStr, ParsePublicKeyError, InvalidLen export {AccountId, BlockHeight, EpochHeight, Balance, StorageUsage, PromiseResult, PromiseError, PromiseIndex, ReceiptIndex, IteratorIndex, Gas, ONE_TERA_GAS, PublicKey, CurveType, curveTypeFromStr, ParsePublicKeyError, InvalidLengthError, Base58Error, UnknownCurve} + +export type GasWeight = bigint; +export const ONE_YOCTO: Balance = 1n; +export const ONE_NEAR: Balance = 1_000_000_000_000_000_000_000_000n; \ No newline at end of file diff --git a/src/types/primitives.ts b/src/types/primitives.ts index 4940813e4..7ee3a2bee 100644 --- a/src/types/primitives.ts +++ b/src/types/primitives.ts @@ -2,3 +2,4 @@ export type StorageUsage = bigint; export type BlockHeight = bigint; export type EpochHeight = bigint; export type Balance = bigint; +export type Nonce = bigint; \ No newline at end of file diff --git a/tests/__tests__/near_bindgen.ava.js b/tests/__tests__/decorators/near_bindgen.ava.js similarity index 100% rename from tests/__tests__/near_bindgen.ava.js rename to tests/__tests__/decorators/near_bindgen.ava.js diff --git a/tests/__tests__/decorators/payable.ava.js b/tests/__tests__/decorators/payable.ava.js new file mode 100644 index 000000000..44c3c2940 --- /dev/null +++ b/tests/__tests__/decorators/payable.ava.js @@ -0,0 +1,61 @@ +import { Worker } from 'near-workspaces'; +import test from 'ava'; + +const DEPOSIT = 1_000_000_000; + +test.beforeEach(async t => { + const worker = await Worker.init(); + const root = worker.rootAccount; + + const payableContract = await root.devDeploy('build/payable.wasm'); + const ali = await root.createSubAccount('ali'); + + t.context.worker = worker; + t.context.accounts = { + root, + payableContract, + ali, + }; +}); + + +test.afterEach(async t => { + await t.context.worker.tearDown().catch(error => { + console.log('Failed to tear down the worker:', error); + }); +}); + +test('payable: true functions works with deposit', async t => { + const { ali, payableContract } = t.context.accounts; + + await t.notThrowsAsync(ali.call(payableContract, 'setValueWithPayableFunction', { value: 'hello' }, { attachedDeposit: DEPOSIT })); + +}) + +test('payable: true functions works without deposit', async t => { + const { ali, payableContract } = t.context.accounts; + + await t.notThrowsAsync(ali.call(payableContract, 'setValueWithPayableFunction', { value: 'hello' })); + +}) + +test('payable: false throws if atach deposit', async t => { + const { ali, payableContract } = t.context.accounts; + + const result = await ali.callRaw(payableContract, 'setValueWithNotPayableFunction', { value: 'hello' }, { attachedDeposit: DEPOSIT }); + + t.assert(result.result.status.Failure.ActionError.kind.FunctionCallError.ExecutionError.includes('Function is not payable')); +}) + +test('payable default throws if atach deposit', async t => { + const { ali, payableContract } = t.context.accounts; + + const result = await ali.callRaw(payableContract, 'setValueWithNotPayableFunctionByDefault', { value: 'hello' }, { attachedDeposit: DEPOSIT }); + t.assert(result.result.status.Failure.ActionError.kind.FunctionCallError.ExecutionError.includes('Function is not payable')); +}) + +test('payable default works without deposit', async t => { + const { ali, payableContract } = t.context.accounts; + + await t.notThrowsAsync(ali.call(payableContract, 'setValueWithNotPayableFunctionByDefault', { value: 'hello' })); +}) diff --git a/tests/__tests__/decorators/private.ava.js b/tests/__tests__/decorators/private.ava.js new file mode 100644 index 000000000..69b4fa509 --- /dev/null +++ b/tests/__tests__/decorators/private.ava.js @@ -0,0 +1,46 @@ +import { Worker } from 'near-workspaces'; +import test from 'ava'; + +test.beforeEach(async t => { + const worker = await Worker.init(); + const root = worker.rootAccount; + + const contract = await root.devDeploy('build/private.wasm'); + const ali = await root.createSubAccount('ali'); + + t.context.worker = worker; + t.context.accounts = { + root, + contract, + ali, + }; +}); + + +test.afterEach(async t => { + await t.context.worker.tearDown().catch(error => { + console.log('Failed to tear down the worker:', error); + }); +}); + +test('private: true throws if called from another acc', async t => { + const { ali, contract } = t.context.accounts; + + const result = await ali.callRaw(contract, 'setValueWithPrivateFunction', { value: 'hello' }); + + t.assert(result.result.status.Failure.ActionError.kind.FunctionCallError.ExecutionError.includes('Function is private')); + +}) + +test('private: true not throws if called from owner acc', async t => { + const { contract } = t.context.accounts; + + await t.notThrowsAsync(contract.call(contract, 'setValueWithNotPrivateFunction', { value: 'hello' })); + +}) + +test('private: default not throws from another acc', async t => { + const { ali, contract } = t.context.accounts; + + await t.notThrowsAsync(ali.call(contract, 'setValueWithNotPrivateFunctionByDefault', { value: 'hello' })); +}) diff --git a/tests/__tests__/test_highlevel_promise.ava.js b/tests/__tests__/test_highlevel_promise.ava.js new file mode 100644 index 000000000..57c0544ba --- /dev/null +++ b/tests/__tests__/test_highlevel_promise.ava.js @@ -0,0 +1,144 @@ +import { Worker } from 'near-workspaces'; +import test from 'ava'; + + +test.before(async t => { + // Init the worker and start a Sandbox server + const worker = await Worker.init(); + + // Prepare sandbox for tests, create accounts, deploy contracts, etx. + const root = worker.rootAccount; + + const highlevelPromise = await root.createSubAccount('highlevel-promise', {initialBalance: '100100N'}); + await highlevelPromise.deploy('build/highlevel-promise.wasm'); + + // Create and deploy callee contract + const calleeContract = await root.createSubAccount('callee-contract'); + await calleeContract.deploy('build/promise_api.wasm'); + + // Test users + const ali = await root.createSubAccount('ali'); + const bob = await root.createSubAccount('bob'); + + // Save state for test runs + t.context.worker = worker; + t.context.accounts = { root, highlevelPromise, ali, bob, calleeContract }; +}); + +test.after(async t => { + await t.context.worker.tearDown().catch(error => { + console.log('Failed to tear down the worker:', error); + }); +}); + +test('highlevel promise create account, transfer', async t => { + const { bob, highlevelPromise } = t.context.accounts; + + let r = await bob.callRaw(highlevelPromise, 'test_promise_batch_create_transfer', '', {gas: '100 Tgas'}); + t.is(r.result.receipts_outcome[1].outcome.executor_id, highlevelPromise.getSubAccount('a').accountId); + t.is(r.result.receipts_outcome[1].outcome.status.SuccessValue, ''); + + let balance = await highlevelPromise.getSubAccount('a').balance() + t.is(balance.total.toString(), '10000000000000000000000000') +}); + +test('highlevel promise stake', async t => { + const { highlevelPromise } = t.context.accounts; + await highlevelPromise.callRaw(highlevelPromise, 'test_promise_batch_stake', '', {gas: '100 Tgas'}); + let balance = await highlevelPromise.balance(); + t.is(balance.staked.toString(), '100000000000000000000000000000'); +}); + +test('highlevel promise add full access key', async t => { + const { bob, highlevelPromise } = t.context.accounts; + let r = await bob.callRaw(highlevelPromise, 'test_promise_add_full_access_key', '', {gas: '100 Tgas'}); + t.is(r.result.status.SuccessValue, ''); +}); + +test('highlevel promise add function call key', async t => { + const { bob, highlevelPromise } = t.context.accounts; + let r = await bob.callRaw(highlevelPromise, 'test_promise_add_function_call_access_key', '', {gas: '100 Tgas'}); + t.is(r.result.status.SuccessValue, ''); +}); + +test('highlevel promise delete account', async t => { + const { bob, highlevelPromise } = t.context.accounts; + let r = await bob.callRaw(highlevelPromise, 'test_delete_account', '', {gas: '100 Tgas'}); + t.is(r.result.status.SuccessValue, ''); + t.is(await highlevelPromise.getSubAccount('e').exists(), false); +}); + +test('highlevel promise then', async t => { + const { ali, highlevelPromise, calleeContract } = t.context.accounts; + let r = await ali.callRaw(highlevelPromise, 'test_promise_then', '', {gas: '70 Tgas'}); + // call the callee + t.is(r.result.receipts_outcome[1].outcome.executor_id, calleeContract.accountId); + t.deepEqual(JSON.parse(Buffer.from(r.result.receipts_outcome[1].outcome.status.SuccessValue, 'base64')), { + currentAccountId: calleeContract.accountId, + signerAccountId: ali.accountId, + predecessorAccountId: highlevelPromise.accountId, + input: 'abc', + }); + + // the callback scheduled by promise_then + t.is(r.result.receipts_outcome[3].outcome.executor_id, highlevelPromise.accountId); + t.deepEqual(JSON.parse(Buffer.from(r.result.receipts_outcome[3].outcome.status.SuccessValue, 'base64')), { + currentAccountId: highlevelPromise.accountId, + signerAccountId: ali.accountId, + predecessorAccountId: highlevelPromise.accountId, + input: '{"callbackArg1":"def"}', + promiseResults: [JSON.stringify({ + currentAccountId: calleeContract.accountId, + signerAccountId: ali.accountId, + predecessorAccountId: highlevelPromise.accountId, + input: 'abc', + })], + callbackArg1: 'def' + }); +}); + +test('highlevel promise and', async t => { + const { ali, highlevelPromise, calleeContract } = t.context.accounts; + let r = await ali.callRaw(highlevelPromise, 'test_promise_and', '', {gas: '150 Tgas'}); + + // console.log(JSON.stringify(r, null, 2)) + // promise and schedule to call the callee + t.is(r.result.receipts_outcome[1].outcome.executor_id, calleeContract.accountId); + t.deepEqual(JSON.parse(Buffer.from(r.result.receipts_outcome[1].outcome.status.SuccessValue, 'base64')), { + currentAccountId: calleeContract.accountId, + signerAccountId: ali.accountId, + predecessorAccountId: highlevelPromise.accountId, + input: 'abc', + }); + + // promise and schedule to call the callee, with different args + t.is(r.result.receipts_outcome[3].outcome.executor_id, calleeContract.accountId); + t.deepEqual(JSON.parse(Buffer.from(r.result.receipts_outcome[3].outcome.status.SuccessValue, 'base64')), { + currentAccountId: calleeContract.accountId, + signerAccountId: ali.accountId, + predecessorAccountId: highlevelPromise.accountId, + input: 'def', + }); + + // the callback scheduled by promise_then on the promise created by promise_and + t.is(r.result.receipts_outcome[5].outcome.executor_id, highlevelPromise.accountId); + t.deepEqual(JSON.parse(Buffer.from(r.result.receipts_outcome[5].outcome.status.SuccessValue, 'base64')), { + currentAccountId: highlevelPromise.accountId, + signerAccountId: ali.accountId, + predecessorAccountId: highlevelPromise.accountId, + input: '{"callbackArg1":"ghi"}', + promiseResults: [JSON.stringify({ + currentAccountId: calleeContract.accountId, + signerAccountId: ali.accountId, + predecessorAccountId: highlevelPromise.accountId, + input: 'abc', + }), JSON.stringify({ + currentAccountId: calleeContract.accountId, + signerAccountId: ali.accountId, + predecessorAccountId: highlevelPromise.accountId, + input: 'def', + })], + callbackArg1: 'ghi', + }); +}); + diff --git a/tests/package.json b/tests/package.json index 1d331f730..437911369 100644 --- a/tests/package.json +++ b/tests/package.json @@ -6,13 +6,14 @@ "type": "module", "scripts": { "postinstall": "cd .. && yarn link && cd tests && yarn link near-sdk-js", - "build": "yarn build:context-api && yarn build:math-api && yarn build:storage-api && yarn build:log-panic-api && yarn build:promise-api && yarn build:promise-batch-api && yarn build:function-params && yarn build:lookup-map && yarn build:lookup-set && yarn build:unordered-map && yarn build:unordered-set && yarn build:vector && yarn build:bytes && yarn build:typescript && yarn build:public-key && yarn build:near-bindgen", + "build": "yarn build:context-api && yarn build:math-api && yarn build:storage-api && yarn build:log-panic-api && yarn build:promise-api && yarn build:promise-batch-api && yarn build:function-params && yarn build:lookup-map && yarn build:lookup-set && yarn build:unordered-map && yarn build:unordered-set && yarn build:vector && yarn build:bytes && yarn build:typescript && yarn build:public-key && yarn build:near-bindgen && yarn build:payable && yarn build:private && yarn build:highlevel-promise", "build:context-api": "near-sdk-js build src/context_api.js build/context_api.wasm", "build:math-api": "near-sdk-js build src/math_api.js build/math_api.wasm", "build:storage-api": "near-sdk-js build src/storage_api.js build/storage_api.wasm", "build:log-panic-api": "near-sdk-js build src/log_panic_api.js build/log_panic_api.wasm", "build:promise-api": "near-sdk-js build src/promise_api.js build/promise_api.wasm", "build:promise-batch-api": "near-sdk-js build src/promise_batch_api.js build/promise_batch_api.wasm", + "build:highlevel-promise": "near-sdk-js build src/highlevel-promise.js build/highlevel-promise.wasm", "build:function-params": "near-sdk-js build src/function-params.js build/function-params.wasm", "build:lookup-map": "near-sdk-js build src/lookup-map.js build/lookup-map.wasm", "build:lookup-set": "near-sdk-js build src/lookup-set.js build/lookup-set.wasm", @@ -22,13 +23,16 @@ "build:bytes": "near-sdk-js build src/bytes.js build/bytes.wasm", "build:typescript": "near-sdk-js build src/typescript.ts build/typescript.wasm", "build:public-key": "near-sdk-js build src/public-key.js build/public-key.wasm", - "build:near-bindgen": "near-sdk-js build src/near_bindgen/require_init_true.ts build/require_init_true.wasm && near-sdk-js build src/near_bindgen/require_init_false.ts build/require_init_false.wasm", + "build:near-bindgen": "near-sdk-js build src/decorators/require_init_true.ts build/require_init_true.wasm && near-sdk-js build src/decorators/require_init_false.ts build/require_init_false.wasm", + "build:payable": "near-sdk-js build src/decorators/payable.ts build/payable.wasm", + "build:private": "near-sdk-js build src/decorators/private.ts build/private.wasm", "test": "ava", "test:context-api": "ava __tests__/test_context_api.ava.js", "test:math-api": "ava __tests__/test_math_api.ava.js", "test:storage-api": "ava __tests__/test_storage_api.ava.js", "test:log-panic-api": "ava __tests__/test_log_panic_api.ava.js", "test:promise-api": "ava __tests__/test_promise_api.ava.js", + "test:highlevel-promise": "ava __tests__/test_highlevel_promise.ava.js", "test:function-params": "ava __tests__/function-params.ava.js", "test:lookup-map": "ava __tests__/lookup-map.ava.js", "test:lookup-set": "ava __tests__/lookup-set.ava.js", @@ -38,7 +42,9 @@ "test:bytes": "ava __tests__/bytes.ava.js", "test:typescript": "ava __tests__/typescript.ava.js", "test:public-key": "ava __tests__/test-public-key.ava.js", - "test:near-bindgen": "ava __tests__/near_bindgen.ava.js" + "test:near-bindgen": "ava __tests__/decorators/near_bindgen.ava.js", + "test:payable": "ava __tests__/decorators/payable.ava.js", + "test:private": "ava __tests__/decorators/private.ava.js" }, "author": "Near Inc ", "license": "Apache-2.0", diff --git a/tests/src/bytes.js b/tests/src/bytes.js index 6f35221b7..56b171805 100644 --- a/tests/src/bytes.js +++ b/tests/src/bytes.js @@ -4,7 +4,7 @@ export function log_expected_input_tests() { // log ascii string near.log('abc') // log string with utf-8 chars - near.log('水') + near.log('水') // Buffer([0xe6, 0xb0, 0xb4]) // log number near.log(333) // log aribrary byte sequence diff --git a/tests/src/decorators/payable.ts b/tests/src/decorators/payable.ts new file mode 100644 index 000000000..d1e6d9819 --- /dev/null +++ b/tests/src/decorators/payable.ts @@ -0,0 +1,38 @@ +import { + near, + NearBindgen, + call, + view, +} from 'near-sdk-js' + +@NearBindgen({}) +class PayableTest { + value: string; + + constructor() { + this.value = ''; + } + + @call({ payableFunction: true }) + setValueWithPayableFunction({ value }: { value: string }): void { + near.log(`payableFunction: ${value}`) + this.value = value; + } + + @call({ payableFunction: false }) + setValueWithNotPayableFunction({ value }: { value: string }): void { + near.log(`notPayableFunction: ${value}`) + this.value = value; + } + + @call({}) + setValueWithNotPayableFunctionByDefault({ value }: { value: string }): void { + near.log(`notPayableFunctionDefault: ${value}`) + this.value = value; + } + + @view({}) + getValue(): string { + return this.value; + } +} diff --git a/tests/src/decorators/private.ts b/tests/src/decorators/private.ts new file mode 100644 index 000000000..f3a6a9018 --- /dev/null +++ b/tests/src/decorators/private.ts @@ -0,0 +1,38 @@ +import { + near, + NearBindgen, + call, + view, +} from 'near-sdk-js' + +@NearBindgen({}) +class PrivateTest { + value: string; + + constructor() { + this.value = ''; + } + + @call({ privateFunction: true }) + setValueWithPrivateFunction({ value }: { value: string }): void { + near.log(`setValueWithPrivateFunction: ${value}`) + this.value = value; + } + + @call({ privateFunction: false }) + setValueWithNotPrivateFunction({ value }: { value: string }): void { + near.log(`setValueWithNotPrivateFunction: ${value}`) + this.value = value; + } + + @call({}) + setValueWithNotPrivateFunctionByDefault({ value }: { value: string }): void { + near.log(`setValueWithNotPrivateFunctionByDefault: ${value}`) + this.value = value; + } + + @view({}) + getValue(): string { + return this.value; + } +} diff --git a/tests/src/near_bindgen/require_init_false.ts b/tests/src/decorators/require_init_false.ts similarity index 92% rename from tests/src/near_bindgen/require_init_false.ts rename to tests/src/decorators/require_init_false.ts index ac348eab9..a8ab954ce 100644 --- a/tests/src/near_bindgen/require_init_false.ts +++ b/tests/src/decorators/require_init_false.ts @@ -14,19 +14,19 @@ class NBTest { this.status = ''; } - @initialize + @initialize({}) init({ status }: { status: string }): void { near.log(`init: ${status}`) this.status = status; } - @view + @view({}) getStatus(): string { near.log(`getStatus: ${this.status}`) return this.status; } - @call + @call({}) setStatus({ status }: { status: string }): void { near.log(`setStatus: ${status}`) this.status = status; diff --git a/tests/src/near_bindgen/require_init_true.ts b/tests/src/decorators/require_init_true.ts similarity index 92% rename from tests/src/near_bindgen/require_init_true.ts rename to tests/src/decorators/require_init_true.ts index 0318086ea..b756f5d8b 100644 --- a/tests/src/near_bindgen/require_init_true.ts +++ b/tests/src/decorators/require_init_true.ts @@ -14,19 +14,19 @@ class NBTest { this.status = ''; } - @initialize + @initialize({}) init({ status }: { status: string }): void { near.log(`init: ${status}`) this.status = status; } - @view + @view({}) getStatus(): string { near.log(`getStatus: ${this.status}`) return this.status; } - @call + @call({}) setStatus({ status }: { status: string }): void { near.log(`setStatus: ${status}`) this.status = status; diff --git a/tests/src/function-params.js b/tests/src/function-params.js index 1156201e2..b6dfa8c02 100644 --- a/tests/src/function-params.js +++ b/tests/src/function-params.js @@ -16,7 +16,7 @@ class FunctionParamsTestContract { this.val3 = 'default3'; } - @call + @call({}) set_values({ param1, param2, param3 }) { near.log(JSON.stringify({ param1, param2, param3 })); this.val1 = param1; @@ -24,7 +24,7 @@ class FunctionParamsTestContract { this.val3 = param3; } - @view + @view({}) get_values() { return { val3: this.val3, val2: this.val2, val1: this.val1 } } diff --git a/tests/src/highlevel-promise.js b/tests/src/highlevel-promise.js new file mode 100644 index 000000000..702dd154d --- /dev/null +++ b/tests/src/highlevel-promise.js @@ -0,0 +1,87 @@ +import {NearBindgen, call, view, NearPromise, near, bytes} from 'near-sdk-js' +import { PublicKey } from 'near-sdk-js/lib/types'; + +function callingData() { + return { + currentAccountId: near.currentAccountId(), + signerAccountId: near.signerAccountId(), + predecessorAccountId: near.predecessorAccountId(), + input: near.input(), + } +} + +function arrayN(n) { + return [...Array(Number(n)).keys()] +} + +@NearBindgen({}) +class HighlevelPromiseContract { + @call({}) + test_promise_batch_stake() { + let promise = NearPromise.new('highlevel-promise.test.near') + .stake(100000000000000000000000000000n, new PublicKey(near.signerAccountPk())) + + return promise; + } + + @call({}) + test_promise_batch_create_transfer() { + let promise = NearPromise.new('a.highlevel-promise.test.near') + .createAccount() + .transfer(10000000000000000000000000n) + return promise; + } + + @call({}) + test_promise_add_full_access_key() { + let promise = NearPromise.new('c.highlevel-promise.test.near') + .createAccount() + .transfer(10000000000000000000000000n) + .addFullAccessKey(new PublicKey(near.signerAccountPk())) + return promise; + } + + @call({}) + test_promise_add_function_call_access_key() { + let promise = NearPromise.new('d.highlevel-promise.test.near') + .createAccount() + .transfer(10000000000000000000000000n) + .addAccessKey(new PublicKey(near.signerAccountPk()), 250000000000000000000000n, 'highlevel-promise.test.near', 'test_promise_batch_create_transfer') + return promise; + } + + @call({}) + test_delete_account() { + let promise = NearPromise.new('e.highlevel-promise.test.near') + .createAccount() + .transfer(10000000000000000000000000n) + .deleteAccount(near.signerAccountId()) + return promise; + } + + @call({}) + test_promise_then() { + let promise = NearPromise.new('callee-contract.test.near') + .functionCall('cross_contract_callee', bytes('abc'), 0, 2 * Math.pow(10, 13)) + .then(NearPromise.new('highlevel-promise.test.near').functionCall('cross_contract_callback', bytes(JSON.stringify({callbackArg1: 'def'})), 0, 2 * Math.pow(10, 13))) + return promise; + } + + @call({}) + test_promise_and() { + let promise = NearPromise.new('callee-contract.test.near') + .functionCall('cross_contract_callee', bytes('abc'), 0, 2 * Math.pow(10, 13)) + let promise2 = NearPromise.new('callee-contract.test.near') + .functionCall('cross_contract_callee', bytes('def'), 0, 2 * Math.pow(10, 13)) + let retPromise = promise.and(promise2).then( + NearPromise.new('highlevel-promise.test.near') + .functionCall('cross_contract_callback', bytes(JSON.stringify({callbackArg1: 'ghi'})), 0, 3 * Math.pow(10, 13))) + + return retPromise; + } + + @call({}) + cross_contract_callback({callbackArg1}) { + return {...callingData(), promiseResults: arrayN(near.promiseResultsCount()).map(i => near.promiseResult(i)), callbackArg1} + } +} \ No newline at end of file diff --git a/tests/src/lookup-map.js b/tests/src/lookup-map.js index a4fb5c795..b57b77b7e 100644 --- a/tests/src/lookup-map.js +++ b/tests/src/lookup-map.js @@ -12,37 +12,37 @@ class LookupMapTestContract { this.lookupMap = new LookupMap('a'); } - @view + @view({}) get({ key }) { return this.lookupMap.get(key); } - @view + @view({}) containsKey({ key }) { return this.lookupMap.containsKey(key); } - @call + @call({}) set({ key, value }) { this.lookupMap.set(key, value); } - @call + @call({}) remove_key({ key }) { this.lookupMap.remove(key); } - @call + @call({}) extend({ kvs }) { this.lookupMap.extend(kvs); } - @call + @call({}) add_house() { this.lookupMap.set('house1', new House('house1', [new Room('room1', '200sqft'), new Room('room2', '300sqft')])) } - @view + @view({}) get_house() { const houseObject = this.lookupMap.get('house1') // restore class object from serialized data diff --git a/tests/src/lookup-set.js b/tests/src/lookup-set.js index e9166e68f..458c3ac87 100644 --- a/tests/src/lookup-set.js +++ b/tests/src/lookup-set.js @@ -12,27 +12,27 @@ class LookupSetTestContract { this.lookupSet = new LookupSet('a'); } - @view + @view({}) contains({ key }) { return this.lookupSet.contains(key); } - @call + @call({}) set({ key }) { this.lookupSet.set(key); } - @call + @call({}) remove_key({ key }) { this.lookupSet.remove(key); } - @call + @call({}) extend({ keys }) { this.lookupSet.extend(keys); } - @call + @call({}) add_house({ name, rooms }) { let house = new House(name, []) for (let r of rooms) { @@ -41,7 +41,7 @@ class LookupSetTestContract { this.lookupSet.set(house) } - @view + @view({}) house_exist({ name, rooms }) { let house = new House(name, []) for (let r of rooms) { diff --git a/tests/src/typescript.ts b/tests/src/typescript.ts index 0ae82c25f..f9478cace 100644 --- a/tests/src/typescript.ts +++ b/tests/src/typescript.ts @@ -5,7 +5,7 @@ import { @NearBindgen({}) class TypeScriptTestContract { - @view + @view({}) bigint() { // JSON.stringify cannot seriaize a BigInt, need manually toString return (1n + 2n).toString() diff --git a/tests/src/unordered-map.js b/tests/src/unordered-map.js index d4bddfaa9..9c8978012 100644 --- a/tests/src/unordered-map.js +++ b/tests/src/unordered-map.js @@ -12,52 +12,52 @@ class UnorderedMapTestContract { this.unorderedMap = new UnorderedMap('a'); } - @view + @view({}) len() { return this.unorderedMap.length; } - @view + @view({}) isEmpty() { return this.unorderedMap.isEmpty(); } - @view + @view({}) get({ key }) { return this.unorderedMap.get(key); } - @call + @call({}) set({ key, value }) { this.unorderedMap.set(key, value); } - @call + @call({}) remove_key({ key }) { this.unorderedMap.remove(key); } - @call + @call({}) clear() { this.unorderedMap.clear(); } - @view + @view({}) toArray() { return this.unorderedMap.toArray(); } - @call + @call({}) extend({ kvs }) { this.unorderedMap.extend(kvs); } - @call + @call({}) add_house() { this.unorderedMap.set('house1', new House('house1', [new Room('room1', '200sqft'), new Room('room2', '300sqft')])) } - @view + @view({}) get_house() { const rawHouse = this.unorderedMap.get('house1') const house = new House(rawHouse.name, rawHouse.rooms) diff --git a/tests/src/unordered-set.js b/tests/src/unordered-set.js index 56a0491a5..b7500c111 100644 --- a/tests/src/unordered-set.js +++ b/tests/src/unordered-set.js @@ -12,48 +12,48 @@ class UnorderedSetTestContract { this.unorderedSet = new UnorderedSet('a'); } - @view + @view({}) len() { return this.unorderedSet.length; } - @view + @view({}) isEmpty() { return this.unorderedSet.isEmpty(); } - @view + @view({}) contains({ element }) { return this.unorderedSet.contains(element); } - @call + @call({}) set({ element }) { this.unorderedSet.set(element); } - @call + @call({}) remove_key({ element }) { this.unorderedSet.remove(element); } - @call + @call({}) clear() { this.unorderedSet.clear(); } - @view + @view({}) toArray() { const res = this.unorderedSet.toArray(); return res; } - @call + @call({}) extend({ elements }) { this.unorderedSet.extend(elements); } - @call + @call({}) add_house({ name, rooms }) { let house = new House(name, []) for (let r of rooms) { @@ -62,7 +62,7 @@ class UnorderedSetTestContract { this.unorderedSet.set(house) } - @view + @view({}) house_exist({ name, rooms }) { let house = new House(name, []) for (let r of rooms) { diff --git a/tests/src/vector.js b/tests/src/vector.js index 07f466b40..ef1570554 100644 --- a/tests/src/vector.js +++ b/tests/src/vector.js @@ -12,62 +12,62 @@ class VectorTestContract { this.vector = new Vector('a'); } - @view + @view({}) len() { return this.vector.length; } - @view + @view({}) isEmpty() { return this.vector.isEmpty(); } - @view + @view({}) get({index}) { return this.vector.get(index); } - @call + @call({}) push({value}) { this.vector.push(value); } - @call + @call({}) pop() { this.vector.pop(); } - @call + @call({}) clear() { this.vector.clear(); } - @view + @view({}) toArray() { return this.vector.toArray(); } - @call + @call({}) extend({kvs}) { this.vector.extend(kvs); } - @call + @call({}) replace({index, value}) { this.vector.replace(index, value); } - @call + @call({}) swapRemove({index}) { this.vector.swapRemove(index); } - @call + @call({}) add_house() { this.vector.push(new House('house1', [new Room('room1', '200sqft'), new Room('room2', '300sqft')])); } - @view + @view({}) get_house() { return this.vector.get(0); }