diff --git a/.travis.yml b/.travis.yml index 703f1c7..2355265 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,3 +3,6 @@ node_js: - "12" - "14" - "16" +script: + - yarn build + - yarn test diff --git a/chain/near.ts b/chain/near.ts new file mode 100644 index 0000000..ae08232 --- /dev/null +++ b/chain/near.ts @@ -0,0 +1,416 @@ +import '../common/eager_offset' +import { Bytes } from '../common/collections' +import { BigInt } from '../common/numbers' + +// Most types from this namespace are direct mappings or adaptations from: +// https://github.com/streamingfast/proto-near/blob/develop/sf/near/codec/v1/codec.proto +export namespace near { + export type CryptoHash = Bytes + + export type Account = string + + export type BlockHeight = u64 + + export type Balance = BigInt + + export type Gas = u64 + + export type ShardId = u64 + + export type NumBlocks = u64 + + export type ProtocolVersion = u32 + + export type Payload = u64 + + export enum CurveKind { + ED25519 = 0, + SECP256K1 = 1, + } + + export class Signature { + constructor( + public kind: CurveKind, + public bytes: Bytes, + ) {} + } + + export class PublicKey { + constructor( + public kind: CurveKind, + public bytes: Bytes, + ) {} + } + + export enum AccessKeyPermissionKind { + FUNCTION_CALL = 0, + FULL_ACCESS = 1, + } + + export class FunctionCallPermission { + constructor( + public allowance: BigInt, + public receiverId: string, + public methodNames: Array, + ) {} + } + + export class FullAccessPermission {} + + export class AccessKeyPermissionValue { + constructor( + public kind: AccessKeyPermissionKind, + public data: Payload, + ) {} + + toFunctionCall(): FunctionCallPermission { + assert(this.kind == AccessKeyPermissionKind.FUNCTION_CALL, 'AccessKeyPermissionValue is not a \'FunctionCall\'.') + return changetype(this.data as u32) + } + + toFullAccess(): FullAccessPermission { + assert(this.kind == AccessKeyPermissionKind.FULL_ACCESS, 'AccessKeyPermissionValue is not a \'FullAccess\'.') + return changetype(this.data as u32) + } + + static fromFunctionCall(input: FunctionCallPermission): AccessKeyPermissionValue { + return new AccessKeyPermissionValue(AccessKeyPermissionKind.FUNCTION_CALL, changetype(input)) + } + + static fromFullAccess(input: FullAccessPermission): AccessKeyPermissionValue { + return new AccessKeyPermissionValue(AccessKeyPermissionKind.FULL_ACCESS, changetype(input)) + } + } + + export class AccessKey { + constructor( + public nonce: u64, + public permission: AccessKeyPermissionValue, + ) {} + } + + export class DataReceiver { + constructor( + public dataId: CryptoHash, + public receiverId: string, + ) {} + } + + export enum ActionKind { + CREATE_ACCOUNT = 0, + DEPLOY_CONTRACT = 1, + FUNCTION_CALL = 2, + TRANSFER = 3, + STAKE = 4, + ADD_KEY = 5, + DELETE_KEY = 6, + DELETE_ACCOUNT = 7, + } + + export class CreateAccountAction {} + + export class DeployContractAction { + constructor( + public codeHash: Bytes, + ) {} + } + + export class FunctionCallAction { + constructor( + public methodName: string, + public args: Bytes, + public gas: u64, + public deposit: BigInt, + ) {} + } + + export class TransferAction { + constructor( + public deposit: BigInt, + ) {} + } + + export class StakeAction { + constructor( + public stake: Balance, + public publicKey: PublicKey, + ) {} + } + + export class AddKeyAction { + constructor( + public publicKey: PublicKey, + public accessKey: AccessKey, + ) {} + } + + export class DeleteKeyAction { + constructor( + public publicKey: PublicKey, + ) {} + } + + export class DeleteAccountAction { + constructor( + public beneficiaryId: Account, + ) {} + } + + export class ActionValue { + constructor( + public kind: ActionKind, + public data: Payload, + ) {} + + toCreateAccount(): CreateAccountAction { + assert(this.kind == ActionKind.CREATE_ACCOUNT, 'ActionValue is not a \'CreateAccount\'.') + return changetype(this.data as u32) + } + + toDeployContract(): DeployContractAction { + assert(this.kind == ActionKind.DEPLOY_CONTRACT, 'ActionValue is not a \'DeployContract\'.') + return changetype(this.data as u32) + } + + toFunctionCall(): FunctionCallAction { + assert(this.kind == ActionKind.FUNCTION_CALL, 'ActionValue is not a \'FunctionCall\'.') + return changetype(this.data as u32) + } + + toTransfer(): TransferAction { + assert(this.kind == ActionKind.TRANSFER, 'ActionValue is not a \'Transfer\'.') + return changetype(this.data as u32) + } + + toStake(): StakeAction { + assert(this.kind == ActionKind.STAKE, 'ActionValue is not a \'Stake\'.') + return changetype(this.data as u32) + } + + toAddKey(): AddKeyAction { + assert(this.kind == ActionKind.ADD_KEY, 'ActionValue is not a \'AddKey\'.') + return changetype(this.data as u32) + } + + toDeleteKey(): DeleteKeyAction { + assert(this.kind == ActionKind.DELETE_KEY, 'ActionValue is not a \'DeleteKey\'.') + return changetype(this.data as u32) + } + + toDeleteAccount(): DeleteAccountAction { + assert(this.kind == ActionKind.DELETE_ACCOUNT, 'ActionValue is not a \'DeleteAccount\'.') + return changetype(this.data as u32) + } + + static fromCreateAccount(input: CreateAccountAction): ActionValue { + return new ActionValue(ActionKind.CREATE_ACCOUNT, changetype(input)) + } + + static fromDeployContract(input: DeployContractAction): ActionValue { + return new ActionValue(ActionKind.DEPLOY_CONTRACT, changetype(input)) + } + + static fromFunctionCall(input: FunctionCallAction): ActionValue { + return new ActionValue(ActionKind.FUNCTION_CALL, changetype(input)) + } + + static fromTransfer(input: TransferAction): ActionValue { + return new ActionValue(ActionKind.TRANSFER, changetype(input)) + } + + static fromStake(input: StakeAction): ActionValue { + return new ActionValue(ActionKind.STAKE, changetype(input)) + } + + static fromAddKey(input: AddKeyAction): ActionValue { + return new ActionValue(ActionKind.ADD_KEY, changetype(input)) + } + + static fromDeleteKey(input: DeleteKeyAction): ActionValue { + return new ActionValue(ActionKind.DELETE_KEY, changetype(input)) + } + + static fromDeleteAccount(input: DeleteAccountAction): ActionValue { + return new ActionValue(ActionKind.DELETE_ACCOUNT, changetype(input)) + } + } + + // We don't map ReceiptData + export class ActionReceipt { + constructor( + // Receipt fields + public predecessorId: string, + public receiverId: string, + public id: CryptoHash, + + // ReceiptAction fields + public signerId: string, + public signerPublicKey: PublicKey, + public gasPrice: BigInt, + public outputDataReceivers: Array, + public inputDataIds: Array, + public actions: Array, + ) {} + } + + export enum SuccessStatusKind { + VALUE = 0, + RECEIPT_ID = 1, + } + + // Doesn't have Value suffix because it has + // VALUE variant/kind, that would be confusing. + export class SuccessStatus { + constructor( + public kind: SuccessStatusKind, + public data: Payload, + ) {} + + toValue(): Bytes { + assert(this.kind == SuccessStatusKind.VALUE, 'SuccessStatus is not a \'Value\'.') + return changetype(this.data as u32) + } + + toReceiptId(): CryptoHash { + assert(this.kind == SuccessStatusKind.RECEIPT_ID, 'SuccessStatus is not a \'ReceiptId\'.') + return changetype(this.data as u32) + } + + static fromValue(input: Bytes): SuccessStatus { + return new SuccessStatus(SuccessStatusKind.VALUE, changetype(input)) + } + + static fromReceiptId(input: CryptoHash): SuccessStatus { + return new SuccessStatus(SuccessStatusKind.RECEIPT_ID, changetype(input)) + } + } + + export enum Direction { + LEFT = 0, + RIGHT = 1, + } + + export class MerklePathItem { + constructor( + public hash: CryptoHash, + public direction: Direction, + ) {} + + @operator('<') + lt(other: MerklePathItem): boolean { + abort("Less than operator isn't supported in MerklePathItem") + return false + } + + @operator('>') + gt(other: MerklePathItem): boolean { + abort("Greater than operator isn't supported in MerklePathItem") + return false + } + + toString(): string { + return `{hash: ${this.hash.toString()}}, direction: ${this.direction.toString()}` + } + } + + export class MerklePath extends Array {} + + export class ExecutionOutcome { + constructor( + public gasBurnt: u64, + public proof: MerklePath, + public blockHash: CryptoHash, + public id: CryptoHash, + public logs: Array, + public receiptIds: Array, + public tokensBurnt: BigInt, + public executorId: string, + public status: SuccessStatus, + ) {} + } + + export class SlashedValidator { + constructor( + public account: Account, + public isDoubleSign: bool, + ) {} + } + + export class BlockHeader { + constructor( + public height: BlockHeight, + public prevHeight: BlockHeight,// Always zero when version < V3 + public blockOrdinal: NumBlocks,// Always zero when version < V3 + public epochId: CryptoHash, + public nextEpochId: CryptoHash, + public chunksIncluded: u64, + public hash: CryptoHash, + public prevHash: CryptoHash, + public timestampNanosec: u64, + public prevStateRoot: CryptoHash, + public chunkReceiptsRoot: CryptoHash, + public chunkHeadersRoot: CryptoHash, + public chunkTxRoot: CryptoHash, + public outcomeRoot: CryptoHash, + public challengesRoot: CryptoHash, + public randomValue: CryptoHash, + public validatorProposals: Array, + public chunkMask: Array, + public gasPrice: Balance, + public totalSupply: Balance, + public challengesResult: Array, + public lastFinalBlock: CryptoHash, + public lastDsFinalBlock: CryptoHash, + public nextBpHash: CryptoHash, + public blockMerkleRoot: CryptoHash, + public epochSyncDataHash: CryptoHash,// Always empty when version < V3 + public approvals: Array,// Array> + public signature: Signature, + public latestProtocolVersion: ProtocolVersion, + ) {} + } + + export class ValidatorStake { + constructor( + public account: Account, + public publicKey: PublicKey, + public stake: Balance, + ) {} + } + + export class ChunkHeader { + constructor( + public encodedLength: u64, + public gasUsed: Gas, + public gasLimit: Gas, + public shardId: ShardId, + public heightCreated: BlockHeight, + public heightIncluded: BlockHeight, + public chunkHash: CryptoHash, + public signature: Signature, + public prevBlockHash: CryptoHash, + public prevStateRoot: CryptoHash, + public encodedMerkleRoot: CryptoHash, + public balanceBurnt: Balance, + public outgoingReceiptsRoot: CryptoHash, + public txRoot: CryptoHash, + public validatorProposals: Array, + ) {} + } + + export class Block { + constructor( + public author: Account, + public header: BlockHeader, + public chunks: Array, + ) {} + } + + export class ReceiptWithOutcome { + constructor( + public outcome: ExecutionOutcome, + public receipt: ActionReceipt, + public block: Block, + ) {} + } +} diff --git a/global/global.ts b/global/global.ts index e92a04d..8a844b0 100644 --- a/global/global.ts +++ b/global/global.ts @@ -2,6 +2,7 @@ import { BigDecimal } from '../common/numbers' import { TypedMapEntry, Entity, TypedMap, Result, Wrapped } from '../common/collections' import { JSONValue, Value } from '../common/value' import { ethereum } from '../chain/ethereum' +import { near } from '../chain/near' export enum TypeId { String = 0, @@ -56,6 +57,41 @@ export enum TypeId { ArrayF32 = 49, ArrayF64 = 50, ArrayBigDecimal = 51, + NearArrayDataReceiver = 52, + NearArrayCryptoHash = 53, + NearArrayActionValue = 54, + NearMerklePath = 55,// or NearArrayMerklePathItem + NearArrayValidatorStake = 56, + NearArraySlashedValidator = 57, + NearArraySignature = 58, + NearArrayChunkHeader = 59, + NearAccessKeyPermissionValue = 60, + NearActionValue = 61, + NearDirection = 62,// not used in graph-node, could be removed + NearPublicKey = 63, + NearSignature = 64, + NearFunctionCallPermission = 65, + NearFullAccessPermission = 66, + NearAccessKey = 67, + NearDataReceiver = 68, + NearCreateAccountAction = 69, + NearDeployContractAction = 70, + NearFunctionCallAction = 71, + NearTransferAction = 72, + NearStakeAction = 73, + NearAddKeyAction = 74, + NearDeleteKeyAction = 75, + NearDeleteAccountAction = 76, + NearActionReceipt = 77, + NearSuccessStatus = 78, + NearMerklePathItem = 79, + NearExecutionOutcome = 80, + NearSlashedValidator = 81, + NearBlockHeader = 82, + NearValidatorStake = 83, + NearChunkHeader = 84, + NearBlock = 85, + NearReceiptWithOutcome = 86, } export function id_of_type(typeId: TypeId): usize { @@ -164,6 +200,77 @@ export function id_of_type(typeId: TypeId): usize { return idof>() case TypeId.ArrayBigDecimal: return idof>() + case TypeId.NearArrayDataReceiver: + return idof>() + case TypeId.NearArrayCryptoHash: + return idof>() + case TypeId.NearArrayActionValue: + return idof>() + case TypeId.NearMerklePath: + return idof() + case TypeId.NearArrayValidatorStake: + return idof>() + case TypeId.NearArraySlashedValidator: + return idof>() + case TypeId.NearArraySignature: + return idof>() + case TypeId.NearArrayChunkHeader: + return idof>() + case TypeId.NearAccessKeyPermissionValue: + return idof() + case TypeId.NearActionValue: + return idof() + // Commented out because it's an enum, there's no type_id + // case TypeId.NearDirection: + // return idof() + case TypeId.NearPublicKey: + return idof() + case TypeId.NearSignature: + return idof() + case TypeId.NearFunctionCallPermission: + return idof() + case TypeId.NearFullAccessPermission: + return idof() + case TypeId.NearAccessKey: + return idof() + case TypeId.NearDataReceiver: + return idof() + case TypeId.NearCreateAccountAction: + return idof() + case TypeId.NearDeployContractAction: + return idof() + case TypeId.NearFunctionCallAction: + return idof() + case TypeId.NearTransferAction: + return idof() + case TypeId.NearStakeAction: + return idof() + case TypeId.NearAddKeyAction: + return idof() + case TypeId.NearDeleteKeyAction: + return idof() + case TypeId.NearDeleteAccountAction: + return idof() + case TypeId.NearActionReceipt: + return idof() + case TypeId.NearSuccessStatus: + return idof() + case TypeId.NearMerklePathItem: + return idof() + case TypeId.NearExecutionOutcome: + return idof() + case TypeId.NearSlashedValidator: + return idof() + case TypeId.NearBlockHeader: + return idof() + case TypeId.NearValidatorStake: + return idof() + case TypeId.NearChunkHeader: + return idof() + case TypeId.NearBlock: + return idof() + case TypeId.NearReceiptWithOutcome: + return idof() default: return 0 } diff --git a/index.ts b/index.ts index b04a0db..07329d8 100644 --- a/index.ts +++ b/index.ts @@ -2,6 +2,8 @@ import './common/eager_offset' // Ethereum support export * from './chain/ethereum' +// NEAR support +export * from './chain/near' // Regular re-exports export * from './common/numbers' export * from './common/collections' diff --git a/package.json b/package.json index 5364509..4932c37 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "glob": "^7.1.2" }, "scripts": { + "build": "asc --explicitStart --exportRuntime --runtime stub index.ts helper-functions.ts --lib graph-ts -b index.wasm", "test": "node test/test.js", "release:patch": "npm version patch && npm publish && git push origin master && git push --tags", "release:minor": "npm version minor && npm publish && git push origin master && git push --tags", diff --git a/test/test.js b/test/test.js index 283e444..f71151c 100644 --- a/test/test.js +++ b/test/test.js @@ -27,6 +27,7 @@ fs.copyFileSync('common/collections.ts', 'test/temp_lib/common/collections.ts') fs.copyFileSync('common/conversion.ts', 'test/temp_lib/common/conversion.ts') fs.copyFileSync('common/value.ts', 'test/temp_lib/common/value.ts') fs.copyFileSync('chain/ethereum.ts', 'test/temp_lib/chain/ethereum.ts') +fs.copyFileSync('chain/near.ts', 'test/temp_lib/chain/near.ts') fs.copyFileSync('index.ts', 'test/temp_lib/index.ts') let output_path = 'test/temp_out/test.wasm' @@ -56,6 +57,7 @@ try { fs.unlinkSync('test/temp_lib/common/json.ts') fs.rmdirSync('test/temp_lib/common') fs.unlinkSync('test/temp_lib/chain/ethereum.ts') + fs.unlinkSync('test/temp_lib/chain/near.ts') fs.rmdirSync('test/temp_lib/chain') fs.rmdirSync('test/temp_lib') fs.unlinkSync('test/temp_out/test.wasm') @@ -73,6 +75,7 @@ try { fs.unlinkSync('test/temp_lib/common/conversion.ts') fs.rmdirSync('test/temp_lib/common') fs.unlinkSync('test/temp_lib/chain/ethereum.ts') + fs.unlinkSync('test/temp_lib/chain/near.ts') fs.rmdirSync('test/temp_lib/chain') fs.rmdirSync('test/temp_lib') fs.unlinkSync('test/temp_out/test.wasm')