From 8924f7e961e6b7f66fc149a7d967b8c58e190c99 Mon Sep 17 00:00:00 2001 From: Asia Date: Wed, 28 Jun 2023 16:03:27 +0200 Subject: [PATCH] feat: interaction data item (#430) --- .gitignore | 1 + bundle.js | 5 +- package.json | 5 +- src/__tests__/unit/signature.test.ts | 66 +++++++------- src/contract/Contract.ts | 5 +- src/contract/HandlerBasedContract.ts | 119 +++++++++++++++++++------- src/contract/Signature.ts | 60 ++++++++----- src/contract/deploy/CreateContract.ts | 3 +- src/legacy/create-interaction-tx.ts | 52 ++++++----- yarn.lock | 30 ++++--- 10 files changed, 228 insertions(+), 118 deletions(-) diff --git a/.gitignore b/.gitignore index 5f50e58..469221a 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,4 @@ bundles/ logs target +metafile.json \ No newline at end of file diff --git a/bundle.js b/bundle.js index 23975bd..17d2b3b 100644 --- a/bundle.js +++ b/bundle.js @@ -2,6 +2,8 @@ const { build } = require('esbuild'); const rimraf = require('rimraf'); const plugin = require('node-stdlib-browser/helpers/esbuild/plugin'); const stdLibBrowser = require('node-stdlib-browser'); +const fs = require('fs'); +const path = require('path'); const clean = async () => { return new Promise((resolve) => { @@ -24,7 +26,7 @@ const runBuild = async () => { }; console.log('Building web bundle esm.'); - await build({ + const result = await build({ ...buildConfig, minify: true, // metafile: true, @@ -34,6 +36,7 @@ const runBuild = async () => { process.exit(1); }); + // fs.writeFileSync(path.join(__dirname, 'metafile.json'), JSON.stringify(result.metafile)); console.log('Building web bundle iife.'); await build({ ...buildConfig, diff --git a/package.json b/package.json index b017872..0c918c3 100644 --- a/package.json +++ b/package.json @@ -89,6 +89,7 @@ "safe-stable-stringify": "2.4.1", "stream-buffers": "^3.0.2", "unzipit": "^1.4.0", + "warp-arbundles": "1.0.1", "warp-isomorphic": "1.0.4", "warp-wasm-metering": "1.0.1" }, @@ -117,8 +118,8 @@ "ts-jest": "^28.0.7", "ts-node": "^10.2.1", "typescript": "^4.9.5", - "warp-contracts-plugin-deploy": "1.0.8-beta.0", - "warp-contracts-plugin-vm2": "1.0.0", + "warp-contracts-plugin-deploy": "1.0.8", + "warp-contracts-plugin-vm2": "1.0.1", "warp-contracts-plugin-vrf": "^1.0.3", "ws": "^8.11.0" }, diff --git a/src/__tests__/unit/signature.test.ts b/src/__tests__/unit/signature.test.ts index 231470d..8cd3b14 100644 --- a/src/__tests__/unit/signature.test.ts +++ b/src/__tests__/unit/signature.test.ts @@ -43,9 +43,7 @@ describe('Wallet', () => { it(`should throw for custom signing function and ethereum signature type`, () => { expect(() => { new Signature(warp, { signer: sampleFunction, type: 'ethereum' }); - }).toThrow( - `Unable to use signing function of type: ethereum when not in mainnet environment or bundling is disabled.` - ); + }).toThrow(`Unable to use signing function with signature of type: ethereum.`); }); }); @@ -70,9 +68,18 @@ describe('Wallet', () => { expect(sut.type).toStrictEqual('arweave'); }); + it(`should throw for custom signing function and arweave signature type`, () => { + expect(() => { + new Signature(warp, { signer: sampleFunction, type: 'arweave' }); + }).toThrow(`Unable to use signing function when bundling is enabled.`); + }); + }); + + describe('in testnet environment when gw url set to arweave', () => { + const warp = WarpFactory.forTestnet(defaultCacheOptions, true); + it(`should set correct signature for custom signing function and arweave signature type`, () => { const sut = new Signature(warp, { signer: sampleFunction, type: 'arweave' }); - expect(sut.signer).toEqual(sampleFunction); expect(sut.type).toEqual('arweave'); }); @@ -104,12 +111,10 @@ describe('Wallet', () => { expect(sut.type).toEqual('arweave'); }); - it(`should throw for custom signing function and arweave signature type`, () => { + it(`should throw for custom signing function and ethereum signature type`, () => { expect(() => { const sut = new Signature(warp, { signer: sampleFunction, type: 'ethereum' }); - }).toThrow( - `Unable to use signing function of type: ethereum when not in mainnet environment or bundling is disabled.` - ); + }).toThrow(`Unable to use signing function with signature of type: ethereum.`); }); }); @@ -132,16 +137,16 @@ describe('Wallet', () => { expect(sut.type).toStrictEqual('arweave'); }); - it(`should set correct signature for custom signing function and arweave signature type`, () => { - const sut = new Signature(warp, { signer: sampleFunction, type: 'arweave' }); - expect(sut.signer.toString().replace(/\s+/g, '')).toEqual(sampleFunction.toString().replace(/\s+/g, '')); - expect(sut.type).toEqual('arweave'); + it(`should throw for custom signing function and ethereum signature type`, () => { + expect(() => { + const sut = new Signature(warp, { signer: sampleFunction, type: 'ethereum' }); + }).toThrow(`Unable to use signing function when bundling is enabled.`); }); - it(`should set correct signature for custom signing function and ethereum signature type`, () => { - const sut = new Signature(warp, { signer: sampleFunction, type: 'ethereum' }); - expect(sut.signer.toString().replace(/\s+/g, '')).toEqual(sampleFunction.toString().replace(/\s+/g, '')); - expect(sut.signer).toEqual(sampleFunction); + it(`should throw for custom signing function and arweave signature type`, () => { + expect(() => { + const sut = new Signature(warp, { signer: sampleFunction, type: 'arweave' }); + }).toThrow(`Unable to use signing function when bundling is enabled.`); }); }); @@ -157,9 +162,9 @@ describe('Wallet', () => { }); it('should call getAddress for customSignature, if getAddress provided', async () => { - const warp = WarpFactory.forMainnet(); + const warp = WarpFactory.forMainnet(defaultCacheOptions, true); const customSignature: CustomSignature = { - type: 'ethereum', + type: 'arweave', signer: sampleFunction, getAddress: () => Promise.resolve('owner') }; @@ -171,39 +176,36 @@ describe('Wallet', () => { }); it('should call getAddress for customSignature, if getAddress NOT provided', async () => { - const warp = WarpFactory.forMainnet(); + const warp = WarpFactory.forMainnet(defaultCacheOptions, true); const customSignature: CustomSignature = { - type: 'ethereum', + type: 'arweave', signer: async (tx) => { - tx.owner = 'owner'; + Promise.resolve(tx); } }; const signature = new Signature(warp, customSignature); const address = await signature.getAddress(); - expect(address).toStrictEqual('owner'); + expect(address).toStrictEqual('47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU'); }); it('should use cached valued from getAddress', async () => { - const warp = WarpFactory.forMainnet(); - const mockedSigner = jest.fn(async (tx) => { - tx.owner = 'owner'; - }); + const warp = WarpFactory.forMainnet(defaultCacheOptions, true); const customSignature: CustomSignature = { - type: 'ethereum', - signer: mockedSigner + type: 'arweave', + signer: async (tx) => { + Promise.resolve(tx); + } }; const signature = new Signature(warp, customSignature); const address = await signature.getAddress(); - expect(address).toStrictEqual('owner'); + expect(address).toStrictEqual('47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU'); const cachedAddress = await signature.getAddress(); - expect(cachedAddress).toStrictEqual('owner'); - - expect(mockedSigner).toBeCalledTimes(1); + expect(cachedAddress).toStrictEqual('47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU'); }); }); }); diff --git a/src/contract/Contract.ts b/src/contract/Contract.ts index c1217a9..9ab27a5 100644 --- a/src/contract/Contract.ts +++ b/src/contract/Contract.ts @@ -7,6 +7,7 @@ import { ArTransfer, Tags, ArWallet } from './deploy/CreateContract'; import { CustomSignature } from './Signature'; import { EvaluationOptionsEvaluator } from './EvaluationOptionsEvaluator'; import { InteractionState } from './states/InteractionState'; +import { Signer } from 'warp-arbundles'; export type BenchmarkStats = { gatewayCommunication: number; stateEvaluation: number; total: number }; @@ -87,9 +88,9 @@ export interface Contract { * i.e. whether called contract's function required "caller" info) * Connecting a signer MUST be done before "writeInteraction" and "bundleInteraction". * - * @param signer - either {@link ArWallet} that will be connected to this contract or custom {@link SigningFunction} + * @param signer - either {@link ArWallet} that will be connected to this contract, custom {@link SigningFunction} or {@link Signer} */ - connect(signature: ArWallet | CustomSignature): Contract; + connect(signature: ArWallet | CustomSignature | Signer): Contract; /** * Allows to set ({@link EvaluationOptions}) diff --git a/src/contract/HandlerBasedContract.ts b/src/contract/HandlerBasedContract.ts index 9db2c6c..eb56036 100644 --- a/src/contract/HandlerBasedContract.ts +++ b/src/contract/HandlerBasedContract.ts @@ -14,13 +14,13 @@ import { InteractionsSorter } from '../core/modules/InteractionsSorter'; import { DefaultEvaluationOptions, EvalStateResult, EvaluationOptions } from '../core/modules/StateEvaluator'; import { WARP_TAGS } from '../core/KnownTags'; import { Warp } from '../core/Warp'; -import { createDummyTx, createInteractionTx } from '../legacy/create-interaction-tx'; +import { createDummyTx, createInteractionTagsList, createInteractionTx } from '../legacy/create-interaction-tx'; import { GQLNodeInterface } from '../legacy/gqlResult'; import { Benchmark } from '../logging/Benchmark'; import { LoggerFactory } from '../logging/LoggerFactory'; import { Evolve } from '../plugins/Evolve'; import { ArweaveWrapper } from '../utils/ArweaveWrapper'; -import { getJsonResponse, sleep, stripTrailingSlash } from '../utils/utils'; +import { getJsonResponse, isBrowser, sleep, stripTrailingSlash } from '../utils/utils'; import { BenchmarkStats, Contract, @@ -35,12 +35,13 @@ import { CustomSignature, Signature } from './Signature'; import { EvaluationOptionsEvaluator } from './EvaluationOptionsEvaluator'; import { WarpFetchWrapper } from '../core/WarpFetchWrapper'; import { Mutex } from 'async-mutex'; -import { TransactionStatusResponse } from '../utils/types/arweave-types'; +import { Tag, TransactionStatusResponse } from '../utils/types/arweave-types'; import { InteractionState } from './states/InteractionState'; import { ContractInteractionState } from './states/ContractInteractionState'; import { Crypto } from 'warp-isomorphic'; import { VrfPluginFunctions } from '../core/WarpPlugin'; import Arweave from 'arweave'; +import { createData, tagsExceedLimit, DataItem, Signer } from 'warp-arbundles'; /** * An implementation of {@link Contract} that is backwards compatible with current style @@ -48,6 +49,7 @@ import Arweave from 'arweave'; * * It requires {@link ExecutorFactory} that is using {@link HandlerApi} generic type. */ + export class HandlerBasedContract implements Contract { private readonly logger = LoggerFactory.INST.create('HandlerBasedContract'); @@ -261,6 +263,7 @@ export class HandlerBasedContract implements Contract { const bundleInteraction = interactionsLoader.type() == 'warp' && !effectiveDisableBundling; this._signature.checkNonArweaveSigningAvailability(bundleInteraction); + this._signature.checkBundlerSignerAvailability(bundleInteraction); if ( bundleInteraction && @@ -274,6 +277,10 @@ export class HandlerBasedContract implements Contract { throw new Error('Vrf generation is only available for bundle interaction'); } + if (!input) { + throw new Error(`Input should be a truthy value: ${JSON.stringify(input)}`); + } + if (bundleInteraction) { return await this.bundleInteraction(input, { tags: effectiveTags, @@ -322,34 +329,86 @@ export class HandlerBasedContract implements Contract { ): Promise { this.logger.info('Bundle interaction input', input); - const interactionTx = await this.createInteraction( + const interactionDataItem = await this.createInteractionDataItem( input, options.tags, emptyTransfer, options.strict, - true, options.vrf ); const response = this._warpFetchWrapper.fetch( - `${stripTrailingSlash(this._evaluationOptions.sequencerUrl)}/gateway/sequencer/register`, + `${stripTrailingSlash(this._evaluationOptions.sequencerUrl)}/gateway/v2/sequencer/register`, { method: 'POST', - body: JSON.stringify(interactionTx), headers: { - 'Accept-Encoding': 'gzip, deflate, br', - 'Content-Type': 'application/json', + 'Content-Type': 'application/octet-stream', Accept: 'application/json' - } + }, + body: interactionDataItem.getRaw() } ); + const dataItemId = await interactionDataItem.id; + return { bundlrResponse: await getJsonResponse(response), - originalTxId: interactionTx.id + originalTxId: dataItemId }; } + private async createInteractionDataItem( + input: Input, + tags: Tags, + transfer: ArTransfer, + strict: boolean, + vrf = false + ) { + if (this._evaluationOptions.internalWrites) { + // it modifies tags + await this.discoverInternalWrites(input, tags, transfer, strict, vrf); + } + + if (vrf) { + tags.push(new Tag(WARP_TAGS.REQUEST_VRF, 'true')); + } + + const interactionTags = createInteractionTagsList( + this._contractTxId, + input, + this.warp.environment === 'testnet', + tags + ); + + if (tagsExceedLimit(interactionTags)) { + throw new Error(`Interaction tags exceed limit of 4096 bytes.`); + } + + const data = Math.random().toString().slice(-4); + const bundlerSigner = this._signature.bundlerSigner; + + if (!bundlerSigner) { + throw new Error( + `Signer not set correctly. If you connect wallet through 'use_wallet', please remember that it only works when bundling is disabled.` + ); + } + + let interactionDataItem: DataItem; + if (isBrowser() && bundlerSigner.signer?.signDataItem) { + interactionDataItem = await bundlerSigner.signDataItem(data, interactionTags); + } else { + interactionDataItem = createData(data, bundlerSigner, { tags: interactionTags }); + await interactionDataItem.sign(bundlerSigner); + } + + // TODO: for ethereum owner is set to public key and not the address!! + if (!this._evaluationOptions.internalWrites && strict) { + await this.checkInteractionInStrictMode(interactionDataItem.owner, input, tags, transfer, strict, vrf); + } + + return interactionDataItem; + } + private async createInteraction( input: Input, tags: Tags, @@ -365,10 +424,7 @@ export class HandlerBasedContract implements Contract { } if (vrf) { - tags.push({ - name: WARP_TAGS.REQUEST_VRF, - value: 'true' - }); + tags.push(new Tag(WARP_TAGS.REQUEST_VRF, 'true')); } const interactionTx = await createInteractionTx( @@ -385,20 +441,28 @@ export class HandlerBasedContract implements Contract { ); if (!this._evaluationOptions.internalWrites && strict) { - const { arweave } = this.warp; - const caller = - this._signature.type == 'arweave' - ? await arweave.wallets.ownerToAddress(interactionTx.owner) - : interactionTx.owner; - const handlerResult = await this.callContract(input, 'write', caller, undefined, tags, transfer, strict, vrf); - if (handlerResult.type !== 'ok') { - throw Error('Cannot create interaction: ' + JSON.stringify(handlerResult.error || handlerResult.errorMessage)); - } + await this.checkInteractionInStrictMode(interactionTx.owner, input, tags, transfer, strict, vrf); } return interactionTx; } + private async checkInteractionInStrictMode( + owner: string, + input: Input, + tags: Tags, + transfer: ArTransfer, + strict: boolean, + vrf: boolean + ) { + const { arweave } = this.warp; + const caller = this._signature.type == 'arweave' ? await arweave.wallets.ownerToAddress(owner) : owner; + const handlerResult = await this.callContract(input, 'write', caller, undefined, tags, transfer, strict, vrf); + if (handlerResult.type !== 'ok') { + throw Error('Cannot create interaction: ' + JSON.stringify(handlerResult.error || handlerResult.errorMessage)); + } + } + txId(): string { return this._contractTxId; } @@ -407,7 +471,7 @@ export class HandlerBasedContract implements Contract { return this._callStack; } - connect(signature: ArWallet | CustomSignature): Contract { + connect(signature: ArWallet | CustomSignature | Signer): Contract { this._signature = new Signature(this.warp, signature); return this; } @@ -975,10 +1039,7 @@ export class HandlerBasedContract implements Contract { this.logger.debug('Callstack', callStack.print()); innerWrites.forEach((contractTxId) => { - tags.push({ - name: WARP_TAGS.INTERACT_WRITE, - value: contractTxId - }); + tags.push(new Tag(WARP_TAGS.INTERACT_WRITE, contractTxId)); }); this.logger.debug('Tags with inner calls', tags); diff --git a/src/contract/Signature.ts b/src/contract/Signature.ts index b5d8f6f..10d0670 100644 --- a/src/contract/Signature.ts +++ b/src/contract/Signature.ts @@ -1,7 +1,9 @@ import { Warp } from '../core/Warp'; import { ArWallet } from './deploy/CreateContract'; import { Transaction } from '../utils/types/arweave-types'; -import { BundlerSigner } from './deploy/DataItem'; +import { JWKInterface } from 'arweave/node/lib/wallet'; +import { ArweaveSigner, Signer as BundlerSigner } from 'warp-arbundles'; +import { isBrowser } from '../utils/utils'; export type SignatureType = 'arweave' | 'ethereum'; export type SigningFunction = (tx: Transaction) => Promise; @@ -16,13 +18,13 @@ Different types which can be used to sign transaction or data item - ArWallet - default option for signing Arweave transactions, either JWKInterface or 'use_wallet' - CustomSignature - object with `signer` field - a custom signing function which takes transaction as a parameter and requires signing it on the client side and `type` field of type SignatureType which indicates the wallet's chain, either 'arweave' or 'ethereum' -- Signer - arbundles specific class which allows to sign data items (only this type can be used when bundling is enabled and data items - are being created) +- Signer - arbundles specific class which allows to sign data items (works with data items - when bundling is enabled) */ export type SignatureProvider = ArWallet | CustomSignature | BundlerSigner; export class Signature { signer: SigningFunction; + bundlerSigner: BundlerSigner; readonly type: SignatureType; readonly warp: Warp; private readonly signatureProviderType: 'CustomSignature' | 'ArWallet' | 'BundlerSigner'; @@ -40,8 +42,11 @@ export class Signature { } else if (this.isValidBundlerSignature(walletOrSignature)) { this.signatureProviderType = 'BundlerSigner'; this.type = decodeBundleSignatureType(walletOrSignature.signatureType); + this.bundlerSigner = walletOrSignature; } else { this.assignArweaveSigner(walletOrSignature); + this.bundlerSigner = + typeof walletOrSignature == 'string' ? null : new ArweaveSigner(walletOrSignature as JWKInterface); this.signatureProviderType = 'ArWallet'; this.type = 'arweave'; } @@ -76,19 +81,22 @@ export class Signature { } } - private async deduceSignerBySigning() { + private async deduceSignerBySigning(): Promise { const { arweave } = this.warp; - const dummyTx = await arweave.createTransaction({ - data: Math.random().toString().slice(-4), - reward: '72600854', - last_tx: 'p7vc1iSP6bvH_fCeUFa9LqoV5qiyW-jdEKouAT0XMoSwrNraB9mgpi29Q10waEpO' - }); - await this.signer(dummyTx); - - if (this.type === 'ethereum') { - return dummyTx.owner; - } else if (this.type === 'arweave') { + if (this.signatureProviderType == 'BundlerSigner') { + try { + return await this.bundlerSigner.getAddress(); + } catch (e) { + throw new Error(`Could not get address from the signer. Is the 'getAddress' implementation correct?'`); + } + } else if (this.signatureProviderType == 'ArWallet' || this.signatureProviderType == 'CustomSignature') { + const dummyTx = await arweave.createTransaction({ + data: Math.random().toString().slice(-4), + reward: '72600854', + last_tx: 'p7vc1iSP6bvH_fCeUFa9LqoV5qiyW-jdEKouAT0XMoSwrNraB9mgpi29Q10waEpO' + }); + await (this.signer as SigningFunction)(dummyTx); return arweave.wallets.ownerToAddress(dummyTx.owner); } else { throw Error('Unknown Signature::type'); @@ -101,6 +109,18 @@ export class Signature { } } + checkBundlerSignerAvailability(bundling: boolean): void { + if (bundling && isBrowser() && this.signatureProviderType != 'BundlerSigner') { + throw new Error(`Only wallet of type 'Signer' is allowed when bundling is enabled and in browser.`); + } + + if ((!bundling || this.warp.environment == 'local') && this.signatureProviderType == 'BundlerSigner') { + throw new Error( + `Only wallet of type 'ArWallet' or 'CustomSignature' is allowed when bundling is disabled or in local environment.` + ); + } + } + private assignArweaveSigner(walletOrSignature) { this.signer = async (tx: Transaction) => { await this.warp.arweave.transactions.sign(tx, walletOrSignature); @@ -108,19 +128,17 @@ export class Signature { } private assertEnvForCustomSigner(signatureType: SignatureType) { - if (signatureType === 'arweave') { - return; + if (this.warp.interactionsLoader.type() === 'warp') { + throw new Error(`Unable to use signing function when bundling is enabled.`); } - if (!['mainnet', 'testnet'].includes(this.warp.environment) || this.warp.interactionsLoader.type() !== 'warp') { - throw new Error( - `Unable to use signing function of type: ${signatureType} when not in mainnet environment or bundling is disabled.` - ); + if (signatureType == 'ethereum') { + throw new Error(`Unable to use signing function with signature of type: ${signatureType}.`); } } private isCustomSignature(signature: SignatureProvider): signature is CustomSignature { - return (signature as CustomSignature).signer !== undefined; + return (signature as CustomSignature).signer !== undefined && (signature as CustomSignature).type !== undefined; } private isValidBundlerSignature(signature: SignatureProvider): signature is BundlerSigner { diff --git a/src/contract/deploy/CreateContract.ts b/src/contract/deploy/CreateContract.ts index 0cc0216..5be7cc2 100644 --- a/src/contract/deploy/CreateContract.ts +++ b/src/contract/deploy/CreateContract.ts @@ -4,8 +4,9 @@ import { EvaluationOptions } from '../../core/modules/StateEvaluator'; import { Source } from './Source'; import { BundlerSigner } from './DataItem'; import { CustomSignature } from '../Signature'; +import { Tag } from '../../utils/types/arweave-types'; -export type Tags = { name: string; value: string }[]; +export type Tags = Tag[]; export type ArWallet = JWKInterface | 'use_wallet'; diff --git a/src/legacy/create-interaction-tx.ts b/src/legacy/create-interaction-tx.ts index b99dda2..523c25f 100644 --- a/src/legacy/create-interaction-tx.ts +++ b/src/legacy/create-interaction-tx.ts @@ -3,14 +3,15 @@ import { SMART_WEAVE_TAGS, WARP_TAGS } from '../core/KnownTags'; import { GQLNodeInterface } from './gqlResult'; import { TagsParser } from '../core/modules/impl/TagsParser'; import { SigningFunction } from '../contract/Signature'; -import { BlockData, CreateTransactionInterface, Transaction } from '../utils/types/arweave-types'; +import { BlockData, CreateTransactionInterface, Tag, Transaction } from '../utils/types/arweave-types'; +import { Tags } from '../contract/deploy/CreateContract'; export async function createInteractionTx( arweave: Arweave, signer: SigningFunction, contractId: string, input: Input, - tags: { name: string; value: string }[], + tags: Tags, target = '', winstonQty = '0', dummy = false, @@ -42,24 +43,8 @@ export async function createInteractionTx( const interactionTx = await arweave.createTransaction(options); - if (!input) { - throw new Error(`Input should be a truthy value: ${JSON.stringify(input)}`); - } - - if (tags && tags.length) { - for (const tag of tags) { - interactionTx.addTag(tag.name.toString(), tag.value.toString()); - } - } - interactionTx.addTag(SMART_WEAVE_TAGS.APP_NAME, 'SmartWeaveAction'); - // use real SDK version here? - interactionTx.addTag(SMART_WEAVE_TAGS.APP_VERSION, '0.3.0'); - interactionTx.addTag(SMART_WEAVE_TAGS.SDK, 'Warp'); - interactionTx.addTag(SMART_WEAVE_TAGS.CONTRACT_TX_ID, contractId); - interactionTx.addTag(SMART_WEAVE_TAGS.INPUT, JSON.stringify(input)); - if (isTestnet) { - interactionTx.addTag(WARP_TAGS.WARP_TESTNET, '1.0.0'); - } + const interactionTags = createInteractionTagsList(contractId, input, isTestnet, tags); + interactionTags.forEach((t) => interactionTx.addTag(t.name, t.value)); if (signer) { await signer(interactionTx); @@ -114,3 +99,30 @@ export function createDummyTx(tx: Transaction, from: string, block: BlockData): bundledIn: null }; } + +export function createInteractionTagsList( + contractId: string, + input: Input, + isTestnet: boolean, + customTags?: Tags +) { + const interactionTags: Tags = []; + + if (customTags && customTags.length) { + for (const customTag of customTags) { + interactionTags.push(new Tag(customTag.name.toString(), customTag.value.toString())); + } + } + + interactionTags.push(new Tag(SMART_WEAVE_TAGS.APP_NAME, 'SmartWeaveAction')); + // use real SDK version here? + interactionTags.push(new Tag(SMART_WEAVE_TAGS.APP_VERSION, '0.3.0')); + interactionTags.push(new Tag(SMART_WEAVE_TAGS.SDK, 'Warp')); + interactionTags.push(new Tag(SMART_WEAVE_TAGS.CONTRACT_TX_ID, contractId)); + interactionTags.push(new Tag(SMART_WEAVE_TAGS.INPUT, JSON.stringify(input))); + if (isTestnet) { + interactionTags.push(new Tag(WARP_TAGS.WARP_TESTNET, '1.0.0')); + } + + return interactionTags; +} diff --git a/yarn.lock b/yarn.lock index 391e0fb..a63bf86 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2365,7 +2365,7 @@ arweave-stream-tx@^1.1.0: dependencies: exponential-backoff "^3.1.0" -arweave@1.13.7: +arweave@1.13.7, arweave@^1.13.7: version "1.13.7" resolved "https://registry.yarnpkg.com/arweave/-/arweave-1.13.7.tgz#cda8534c833baec372a7052c61f53b4e39a886d7" integrity sha512-Hv+x2bSI6UyBHpuVbUDMMpMje1ETfpJWj52kKfz44O0IqDRi/LukOkkDUptup1p6OT6KP1/DdpnUnsNHoskFeA== @@ -7472,19 +7472,29 @@ walker@^1.0.8: dependencies: makeerror "1.0.12" -warp-contracts-plugin-deploy@1.0.8-beta.0: - version "1.0.8-beta.0" - resolved "https://registry.yarnpkg.com/warp-contracts-plugin-deploy/-/warp-contracts-plugin-deploy-1.0.8-beta.0.tgz#c4c95a57d53a5ae2a50c0907f94ac74e6bcdbada" - integrity sha512-bA8K9QHuIvE1XzWpFYmAR5+xisanAzfInxx0ekMwlGVFiDWciWcVAFNefLUa5iFHuHlDpqgDQvUpsd6lg7Wx8g== +warp-arbundles@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/warp-arbundles/-/warp-arbundles-1.0.1.tgz#c816ee44aacd90e0214b1b7db8028c6d5b468770" + integrity sha512-bWyF8PoS5D2M8yk7YAT/DFi4pzoPxfXaS1o9eEelcD5SU42PW/wVxwzrvn6pLaHFIK5Zys15V2eGd+8X1Ty62Q== + dependencies: + arweave "^1.13.7" + base64url "^3.0.1" + buffer "^6.0.3" + warp-isomorphic "^1.0.4" + +warp-contracts-plugin-deploy@1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/warp-contracts-plugin-deploy/-/warp-contracts-plugin-deploy-1.0.8.tgz#94e9841ea3f68d09e9c1bbd483efa84a2c541901" + integrity sha512-5KF12X0g2L1BrGGWGidkJAex3AK0c3y3KQDT1J/5OGCmldgesogKOKADw4VrzcdOvV/CbjdyiIXy5XB9cYXPGQ== dependencies: arbundles "^0.7.3" arlocal "^1.1.59" node-stdlib-browser "^1.2.0" -warp-contracts-plugin-vm2@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/warp-contracts-plugin-vm2/-/warp-contracts-plugin-vm2-1.0.0.tgz#f55914a228102f3bda7dafc197c395078fa0e190" - integrity sha512-3XubaZ9eeRbBtMDwmTAewZk6WYgtcg9g44vATqH5vmn2wdPWIBcrBX3gA7XQIDbqPcLPpIgifMDZiQJSNwYZDA== +warp-contracts-plugin-vm2@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/warp-contracts-plugin-vm2/-/warp-contracts-plugin-vm2-1.0.1.tgz#0c1749f998508e8559a4627afc574ff80577353c" + integrity sha512-ogECglCyZZ3vF7nm9RqkDA+b8AwHmoda8Uv2oYIMyxm7haLjTMtZNY+QvqDbI3b/B/MR+nX1nwdTe7KIUqqQjQ== dependencies: bignumber.js "^9.1.1" vm2 "^3.9.16" @@ -7505,7 +7515,7 @@ warp-isomorphic@1.0.0: buffer "^6.0.3" undici "^5.8.0" -warp-isomorphic@1.0.4: +warp-isomorphic@1.0.4, warp-isomorphic@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/warp-isomorphic/-/warp-isomorphic-1.0.4.tgz#1017eba260e0f0228b33b94b9e36a1afe54e09d8" integrity sha512-W77IoLjq/eu5bY1uRrlmVt5lLDoIHeZ0ozJ/j67FTnxvZRXu887biEnom1nx8q1UgOKyJh8eQYFQaE2FLlKhFg==