feat: deploy contract using evm signature (#257)

* feat: deploy contract using evm signature

* fix: review fixes
This commit is contained in:
Asia
2022-11-24 16:34:02 +01:00
committed by GitHub
parent 0503f473fe
commit cc0791bea0
9 changed files with 244 additions and 78 deletions

View File

@@ -0,0 +1,155 @@
import { Signature } from '../../contract/Signature';
import { defaultCacheOptions, WarpFactory } from '../../core/WarpFactory';
describe('Wallet', () => {
const sampleFunction = async () => {
setTimeout(() => {
//test
}, 1000);
};
const signingFunction = `async (tx) => {await this.warp.arweave.transactions.sign(tx, walletOrSignature);}`.replace(
/\s+/g,
''
);
describe('in local environment', () => {
const warp = WarpFactory.forLocal();
it(`should set correct signature for 'use_wallet'`, () => {
const sut = new Signature(warp, 'use_wallet');
expect(sut.signer.toString().replace(/\s+/g, '')).toEqual(signingFunction);
expect(sut.type).toStrictEqual('arweave');
});
it(`should set correct signature for jwk`, () => {
const sut = new Signature(warp, {
kty: '',
e: '',
n: ''
});
expect(sut.signer.toString().replace(/\s+/g, '')).toEqual(signingFunction);
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(() => {
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.`
);
});
});
describe('in testnet environment', () => {
const warp = WarpFactory.forTestnet();
it(`should set correct signature for 'use_wallet'`, () => {
const sut = new Signature(warp, 'use_wallet');
expect(sut.signer.toString().replace(/\s+/g, '')).toEqual(signingFunction);
expect(sut.type).toStrictEqual('arweave');
});
it(`should set correct signature for jwk`, () => {
const sut = new Signature(warp, {
kty: '',
e: '',
n: ''
});
expect(sut.signer.toString().replace(/\s+/g, '')).toEqual(signingFunction);
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).toEqual(sampleFunction);
expect(sut.type).toEqual('arweave');
});
it(`should throw for custom signing function and arweave 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.`
);
});
});
describe('in mainnet environment when bundling is disabled', () => {
const warp = WarpFactory.forMainnet(defaultCacheOptions, true);
it(`should set correct signature for 'use_wallet'`, () => {
const sut = new Signature(warp, 'use_wallet');
expect(sut.signer.toString().replace(/\s+/g, '')).toEqual(signingFunction);
expect(sut.type).toStrictEqual('arweave');
});
it(`should set correct signature for jwk`, () => {
const sut = new Signature(warp, {
kty: '',
e: '',
n: ''
});
expect(sut.signer.toString().replace(/\s+/g, '')).toEqual(signingFunction);
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 arweave 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.`
);
});
});
describe('in mainnet environment when bundling is enabled', () => {
const warp = WarpFactory.forMainnet();
it(`should set correct signature for 'use_wallet'`, () => {
const sut = new Signature(warp, 'use_wallet');
expect(sut.signer.toString().replace(/\s+/g, '')).toEqual(signingFunction);
expect(sut.type).toStrictEqual('arweave');
});
it(`should set correct signature for jwk`, () => {
const sut = new Signature(warp, {
kty: '',
e: '',
n: ''
});
expect(sut.signer.toString().replace(/\s+/g, '')).toEqual(signingFunction);
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 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);
});
});
});

View File

@@ -1,4 +1,3 @@
import Transaction from 'arweave/node/lib/transaction';
import { SortKeyCacheResult } from '../cache/SortKeyCache'; import { SortKeyCacheResult } from '../cache/SortKeyCache';
import { ContractCallRecord } from '../core/ContractCallRecord'; import { ContractCallRecord } from '../core/ContractCallRecord';
import { InteractionResult } from '../core/modules/impl/HandlerExecutorFactory'; import { InteractionResult } from '../core/modules/impl/HandlerExecutorFactory';
@@ -6,12 +5,11 @@ import { EvaluationOptions, EvalStateResult } from '../core/modules/StateEvaluat
import { GQLNodeInterface } from '../legacy/gqlResult'; import { GQLNodeInterface } from '../legacy/gqlResult';
import { ArTransfer, Tags, ArWallet } from './deploy/CreateContract'; import { ArTransfer, Tags, ArWallet } from './deploy/CreateContract';
import { Source } from './deploy/Source'; import { Source } from './deploy/Source';
import { SignatureType } from './Signature';
export type CurrentTx = { interactionTxId: string; contractTxId: string }; export type CurrentTx = { interactionTxId: string; contractTxId: string };
export type BenchmarkStats = { gatewayCommunication: number; stateEvaluation: number; total: number }; export type BenchmarkStats = { gatewayCommunication: number; stateEvaluation: number; total: number };
export type SigningFunction = (tx: Transaction) => Promise<void>;
export type Signature = { signer: SigningFunction; signatureType: 'arweave' | 'ethereum' };
export class ContractError extends Error { export class ContractError extends Error {
constructor(message) { constructor(message) {
super(message); super(message);
@@ -85,7 +83,7 @@ export interface Contract<State = unknown> extends Source {
* *
* @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 or custom {@link SigningFunction}
*/ */
connect(signature: ArWallet | Signature): Contract<State>; connect(signature: ArWallet | SignatureType): Contract<State>;
/** /**
* Allows to set ({@link EvaluationOptions}) * Allows to set ({@link EvaluationOptions})

View File

@@ -1,7 +1,6 @@
import { TransactionStatusResponse } from 'arweave/node/transactions'; import { TransactionStatusResponse } from 'arweave/node/transactions';
import stringify from 'safe-stable-stringify'; import stringify from 'safe-stable-stringify';
import * as crypto from 'crypto'; import * as crypto from 'crypto';
import Transaction from 'arweave/node/lib/transaction';
import { SortKeyCacheResult } from '../cache/SortKeyCache'; import { SortKeyCacheResult } from '../cache/SortKeyCache';
import { ContractCallRecord, InteractionCall } from '../core/ContractCallRecord'; import { ContractCallRecord, InteractionCall } from '../core/ContractCallRecord';
import { ExecutionContext } from '../core/ExecutionContext'; import { ExecutionContext } from '../core/ExecutionContext';
@@ -29,13 +28,13 @@ import {
CurrentTx, CurrentTx,
WriteInteractionOptions, WriteInteractionOptions,
WriteInteractionResponse, WriteInteractionResponse,
InnerCallData, InnerCallData
Signature
} from './Contract'; } from './Contract';
import { Tags, ArTransfer, emptyTransfer, ArWallet } from './deploy/CreateContract'; import { Tags, ArTransfer, emptyTransfer, ArWallet } from './deploy/CreateContract';
import { SourceData, SourceImpl } from './deploy/impl/SourceImpl'; import { SourceData, SourceImpl } from './deploy/impl/SourceImpl';
import { InnerWritesEvaluator } from './InnerWritesEvaluator'; import { InnerWritesEvaluator } from './InnerWritesEvaluator';
import { generateMockVrf } from '../utils/vrf'; import { generateMockVrf } from '../utils/vrf';
import { Signature, SignatureType } from './Signature';
/** /**
* An implementation of {@link Contract} that is backwards compatible with current style * An implementation of {@link Contract} that is backwards compatible with current style
@@ -54,11 +53,7 @@ export class HandlerBasedContract<State> implements Contract<State> {
private readonly _arweaveWrapper: ArweaveWrapper; private readonly _arweaveWrapper: ArweaveWrapper;
private _sorter: InteractionsSorter; private _sorter: InteractionsSorter;
private _rootSortKey: string; private _rootSortKey: string;
private signature: Signature;
/**
* wallet connected to this contract
*/
protected signature?: Signature;
constructor( constructor(
private readonly _contractTxId: string, private readonly _contractTxId: string,
@@ -229,11 +224,7 @@ export class HandlerBasedContract<State> implements Contract<State> {
const bundleInteraction = interactionsLoader.type() == 'warp' && !effectiveDisableBundling; const bundleInteraction = interactionsLoader.type() == 'warp' && !effectiveDisableBundling;
if (this.signature.signatureType !== 'arweave' && !bundleInteraction) { this.signature.checkNonArweaveSigningAvailability(bundleInteraction);
throw new Error(
`Unable to use signing function of type: ${this.signature.signatureType} when not in mainnet environment or bundling is disabled.`
);
}
if ( if (
bundleInteraction && bundleInteraction &&
@@ -404,29 +395,8 @@ export class HandlerBasedContract<State> implements Contract<State> {
return this._callStack; return this._callStack;
} }
connect(signature: ArWallet | Signature): Contract<State> { connect(signature: ArWallet | SignatureType): Contract<State> {
if (this.isSignatureType(signature)) { this.signature = new Signature(this.warp, signature);
if (
signature.signatureType !== 'arweave' &&
(!(this.warp.environment == 'mainnet') || !(this.warp.interactionsLoader.type() == 'warp'))
) {
throw new Error(
`Unable to use signing function of type: ${signature.signatureType} when not in mainnet environment or bundling is disabled.`
);
} else {
this.signature = {
signer: signature.signer,
signatureType: signature.signatureType
};
}
} else {
this.signature = {
signer: async (tx: Transaction) => {
await this.warp.arweave.transactions.sign(tx, signature);
},
signatureType: 'arweave'
};
}
return this; return this;
} }
@@ -788,10 +758,9 @@ export class HandlerBasedContract<State> implements Contract<State> {
if (!this.signature) { if (!this.signature) {
throw new Error("Wallet not connected. Use 'connect' method first."); throw new Error("Wallet not connected. Use 'connect' method first.");
} }
const { arweave } = this.warp; const source = new SourceImpl(this.warp);
const source = new SourceImpl(arweave);
const srcTx = await source.save(sourceData, this.warp.environment, this.signature.signer); const srcTx = await source.save(sourceData, this.warp.environment, this.signature);
return srcTx.id; return srcTx.id;
} }
@@ -799,8 +768,4 @@ export class HandlerBasedContract<State> implements Contract<State> {
get rootSortKey(): string { get rootSortKey(): string {
return this._rootSortKey; return this._rootSortKey;
} }
private isSignatureType(signature: ArWallet | Signature): signature is Signature {
return (signature as Signature).signer !== undefined;
}
} }

45
src/contract/Signature.ts Normal file
View File

@@ -0,0 +1,45 @@
import Transaction from 'arweave/node/lib/transaction';
import { Warp } from 'core/Warp';
import { ArWallet } from './deploy/CreateContract';
export type SigningFunction = (tx: Transaction) => Promise<void>;
export type SignatureType = { signer: SigningFunction; type: 'arweave' | 'ethereum' };
export class Signature {
readonly signer: SigningFunction;
readonly type: 'arweave' | 'ethereum';
readonly warp: Warp;
constructor(warp: Warp, walletOrSignature: ArWallet | SignatureType) {
this.warp = warp;
if (this.isSignatureType(walletOrSignature)) {
if (
walletOrSignature.type !== 'arweave' &&
(!(this.warp.environment == 'mainnet') || !(this.warp.interactionsLoader.type() == 'warp'))
) {
throw new Error(
`Unable to use signing function of type: ${walletOrSignature.type} when not in mainnet environment or bundling is disabled.`
);
} else {
this.signer = walletOrSignature.signer;
this.type = walletOrSignature.type;
}
} else {
this.signer = async (tx: Transaction) => {
await this.warp.arweave.transactions.sign(tx, walletOrSignature);
};
this.type = 'arweave';
}
}
checkNonArweaveSigningAvailability(bundling: boolean): void {
if (this.type !== 'arweave' && !bundling) {
throw new Error(`Unable to use signing function of type: ${this.type} when bundling is disabled.`);
}
}
private isSignatureType(signature: ArWallet | SignatureType): signature is SignatureType {
return (signature as SignatureType).signer !== undefined;
}
}

View File

@@ -1,4 +1,5 @@
import { JWKInterface } from 'arweave/node/lib/wallet'; import { JWKInterface } from 'arweave/node/lib/wallet';
import { SignatureType } from '../../contract/Signature';
export type Tags = { name: string; value: string }[]; export type Tags = { name: string; value: string }[];
@@ -17,7 +18,7 @@ export const emptyTransfer: ArTransfer = {
}; };
export interface CommonContractData { export interface CommonContractData {
wallet: ArWallet; wallet: ArWallet | SignatureType;
initState: string; initState: string;
tags?: Tags; tags?: Tags;
transfer?: ArTransfer; transfer?: ArTransfer;

View File

@@ -1,7 +1,7 @@
import { SigningFunction } from '../../contract/Contract';
import { ArWallet } from './CreateContract'; import { ArWallet } from './CreateContract';
import { SourceData } from './impl/SourceImpl'; import { SourceData } from './impl/SourceImpl';
import { WarpEnvironment } from '../../core/Warp'; import { WarpEnvironment } from '../../core/Warp';
import { SignatureType } from '../../contract/Signature';
export interface Source { export interface Source {
/** /**
@@ -11,7 +11,7 @@ export interface Source {
save( save(
contractSource: SourceData, contractSource: SourceData,
env: WarpEnvironment, env: WarpEnvironment,
signer?: ArWallet | SigningFunction, signer?: ArWallet | SignatureType,
useBundler?: boolean useBundler?: boolean
): Promise<string | null>; ): Promise<string | null>;
} }

View File

@@ -1,6 +1,7 @@
/* eslint-disable */ /* eslint-disable */
import Arweave from 'arweave'; import Arweave from 'arweave';
import Transaction from 'arweave/node/lib/transaction'; import Transaction from 'arweave/node/lib/transaction';
import { Signature } from '../../../contract/Signature';
import { SmartWeaveTags } from '../../../core/SmartWeaveTags'; import { SmartWeaveTags } from '../../../core/SmartWeaveTags';
import { Warp } from '../../../core/Warp'; import { Warp } from '../../../core/Warp';
import { WARP_GW_URL } from '../../../core/WarpFactory'; import { WARP_GW_URL } from '../../../core/WarpFactory';
@@ -11,6 +12,7 @@ import { Buffer } from 'redstone-isomorphic';
export class DefaultCreateContract implements CreateContract { export class DefaultCreateContract implements CreateContract {
private readonly logger = LoggerFactory.INST.create('DefaultCreateContract'); private readonly logger = LoggerFactory.INST.create('DefaultCreateContract');
private signature: Signature;
constructor(private readonly arweave: Arweave, private warp: Warp) { constructor(private readonly arweave: Arweave, private warp: Warp) {
this.deployFromSourceTx = this.deployFromSourceTx.bind(this); this.deployFromSourceTx = this.deployFromSourceTx.bind(this);
@@ -22,7 +24,7 @@ export class DefaultCreateContract implements CreateContract {
const effectiveUseBundler = const effectiveUseBundler =
disableBundling == undefined ? this.warp.definitionLoader.type() == 'warp' : !disableBundling; disableBundling == undefined ? this.warp.definitionLoader.type() == 'warp' : !disableBundling;
const source = new SourceImpl(this.arweave); const source = new SourceImpl(this.warp);
const srcTx = await source.save(contractData, this.warp.environment, wallet, effectiveUseBundler); const srcTx = await source.save(contractData, this.warp.environment, wallet, effectiveUseBundler);
this.logger.debug('Creating new contract'); this.logger.debug('Creating new contract');
@@ -48,22 +50,23 @@ export class DefaultCreateContract implements CreateContract {
): Promise<ContractDeploy> { ): Promise<ContractDeploy> {
this.logger.debug('Creating new contract from src tx'); this.logger.debug('Creating new contract from src tx');
const { wallet, srcTxId, initState, tags, transfer, data } = contractData; const { wallet, srcTxId, initState, tags, transfer, data } = contractData;
this.signature = new Signature(this.warp, wallet);
const signer = this.signature.signer;
const effectiveUseBundler = const effectiveUseBundler =
disableBundling == undefined ? this.warp.definitionLoader.type() == 'warp' : !disableBundling; disableBundling == undefined ? this.warp.definitionLoader.type() == 'warp' : !disableBundling;
let contractTX = await this.arweave.createTransaction({ data: data?.body || initState }, wallet); this.signature.checkNonArweaveSigningAvailability(effectiveUseBundler);
let contractTX = await this.arweave.createTransaction({ data: data?.body || initState });
if (+transfer?.winstonQty > 0 && transfer.target.length) { if (+transfer?.winstonQty > 0 && transfer.target.length) {
this.logger.debug('Creating additional transaction with AR transfer', transfer); this.logger.debug('Creating additional transaction with AR transfer', transfer);
contractTX = await this.arweave.createTransaction( contractTX = await this.arweave.createTransaction({
{ data: data?.body || initState,
data: data?.body || initState, target: transfer.target,
target: transfer.target, quantity: transfer.winstonQty
quantity: transfer.winstonQty });
},
wallet
);
} }
if (tags?.length) { if (tags?.length) {
@@ -86,7 +89,7 @@ export class DefaultCreateContract implements CreateContract {
contractTX.addTag(SmartWeaveTags.WARP_TESTNET, '1.0.0'); contractTX.addTag(SmartWeaveTags.WARP_TESTNET, '1.0.0');
} }
await this.arweave.transactions.sign(contractTX, wallet); await signer(contractTX);
let responseOk: boolean; let responseOk: boolean;
let response: { status: number; statusText: string; data: any }; let response: { status: number; statusText: string; data: any };

View File

@@ -1,16 +1,15 @@
/* eslint-disable */ /* eslint-disable */
import metering from 'redstone-wasm-metering'; import metering from 'redstone-wasm-metering';
import Arweave from 'arweave';
import { Go } from '../../../core/modules/impl/wasm/go-wasm-imports'; import { Go } from '../../../core/modules/impl/wasm/go-wasm-imports';
import fs, { PathOrFileDescriptor } from 'fs'; import fs, { PathOrFileDescriptor } from 'fs';
import { matchMutClosureDtor } from '../../../core/modules/impl/wasm/wasm-bindgen-tools'; import { matchMutClosureDtor } from '../../../core/modules/impl/wasm/wasm-bindgen-tools';
import { ArWallet, ContractType } from '../CreateContract'; import { ArWallet, ContractType } from '../CreateContract';
import { SigningFunction } from '../../../contract/Contract';
import { SmartWeaveTags } from '../../../core/SmartWeaveTags'; import { SmartWeaveTags } from '../../../core/SmartWeaveTags';
import { LoggerFactory } from '../../../logging/LoggerFactory'; import { LoggerFactory } from '../../../logging/LoggerFactory';
import { Source } from '../Source'; import { Source } from '../Source';
import { Buffer } from 'redstone-isomorphic'; import { Buffer } from 'redstone-isomorphic';
import { WarpEnvironment } from '../../../core/Warp'; import { Warp, WarpEnvironment } from '../../../core/Warp';
import { Signature, SignatureType } from '../../../contract/Signature';
const wasmTypeMapping: Map<number, string> = new Map([ const wasmTypeMapping: Map<number, string> = new Map([
[1, 'assemblyscript'], [1, 'assemblyscript'],
@@ -28,18 +27,25 @@ export interface SourceData {
export class SourceImpl implements Source { export class SourceImpl implements Source {
private readonly logger = LoggerFactory.INST.create('Source'); private readonly logger = LoggerFactory.INST.create('Source');
constructor(private readonly arweave: Arweave) {} private signature: Signature;
constructor(private readonly warp: Warp) {}
async save( async save(
contractData: SourceData, contractData: SourceData,
env: WarpEnvironment, env: WarpEnvironment,
signer: ArWallet | SigningFunction, signature: ArWallet | SignatureType,
useBundler = false useBundler = false
): Promise<any> { ): Promise<any> {
this.logger.debug('Creating new contract source'); this.logger.debug('Creating new contract source');
const { src, wasmSrcCodeDir, wasmGlueCode } = contractData; const { src, wasmSrcCodeDir, wasmGlueCode } = contractData;
this.signature = new Signature(this.warp, signature);
const signer = this.signature.signer;
this.signature.checkNonArweaveSigningAvailability(useBundler);
const contractType: ContractType = src instanceof Buffer ? 'wasm' : 'js'; const contractType: ContractType = src instanceof Buffer ? 'wasm' : 'js';
let srcTx; let srcTx;
let wasmLang = null; let wasmLang = null;
@@ -102,11 +108,7 @@ export class SourceImpl implements Source {
const allData = contractType == 'wasm' ? this.joinBuffers(data) : src; const allData = contractType == 'wasm' ? this.joinBuffers(data) : src;
if (typeof signer == 'function') { srcTx = await this.warp.arweave.createTransaction({ data: allData });
srcTx = await this.arweave.createTransaction({ data: allData });
} else {
srcTx = await this.arweave.createTransaction({ data: allData }, signer);
}
srcTx.addTag(SmartWeaveTags.APP_NAME, 'SmartWeaveContractSource'); srcTx.addTag(SmartWeaveTags.APP_NAME, 'SmartWeaveContractSource');
// TODO: version should be taken from the current package.json version. // TODO: version should be taken from the current package.json version.
@@ -124,11 +126,8 @@ export class SourceImpl implements Source {
srcTx.addTag(SmartWeaveTags.WARP_TESTNET, '1.0.0'); srcTx.addTag(SmartWeaveTags.WARP_TESTNET, '1.0.0');
} }
if (typeof signer == 'function') { await signer(srcTx);
await signer(srcTx);
} else {
await this.arweave.transactions.sign(srcTx, signer);
}
this.logger.debug('Posting transaction with source'); this.logger.debug('Posting transaction with source');
// note: in case of useBundler = true, we're posting both // note: in case of useBundler = true, we're posting both
@@ -136,7 +135,7 @@ export class SourceImpl implements Source {
let responseOk = true; let responseOk = true;
let response: { status: number; statusText: string; data: any }; let response: { status: number; statusText: string; data: any };
if (!useBundler) { if (!useBundler) {
response = await this.arweave.transactions.post(srcTx); response = await this.warp.arweave.transactions.post(srcTx);
responseOk = response.status === 200 || response.status === 208; responseOk = response.status === 200 || response.status === 208;
} }

View File

@@ -4,8 +4,8 @@ import { CreateTransactionInterface } from 'arweave/node/common';
import { BlockData } from 'arweave/node/blocks'; import { BlockData } from 'arweave/node/blocks';
import { SmartWeaveTags } from '../core/SmartWeaveTags'; import { SmartWeaveTags } from '../core/SmartWeaveTags';
import { GQLNodeInterface } from './gqlResult'; import { GQLNodeInterface } from './gqlResult';
import { SigningFunction } from '../contract/Contract';
import { TagsParser } from '../core/modules/impl/TagsParser'; import { TagsParser } from '../core/modules/impl/TagsParser';
import { SigningFunction } from '../contract/Signature';
export async function createInteractionTx( export async function createInteractionTx(
arweave: Arweave, arweave: Arweave,