feat: interaction data item (#430)
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -33,3 +33,4 @@ bundles/
|
|||||||
logs
|
logs
|
||||||
|
|
||||||
target
|
target
|
||||||
|
metafile.json
|
||||||
@@ -2,6 +2,8 @@ const { build } = require('esbuild');
|
|||||||
const rimraf = require('rimraf');
|
const rimraf = require('rimraf');
|
||||||
const plugin = require('node-stdlib-browser/helpers/esbuild/plugin');
|
const plugin = require('node-stdlib-browser/helpers/esbuild/plugin');
|
||||||
const stdLibBrowser = require('node-stdlib-browser');
|
const stdLibBrowser = require('node-stdlib-browser');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
const clean = async () => {
|
const clean = async () => {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
@@ -24,7 +26,7 @@ const runBuild = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
console.log('Building web bundle esm.');
|
console.log('Building web bundle esm.');
|
||||||
await build({
|
const result = await build({
|
||||||
...buildConfig,
|
...buildConfig,
|
||||||
minify: true,
|
minify: true,
|
||||||
// metafile: true,
|
// metafile: true,
|
||||||
@@ -34,6 +36,7 @@ const runBuild = async () => {
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// fs.writeFileSync(path.join(__dirname, 'metafile.json'), JSON.stringify(result.metafile));
|
||||||
console.log('Building web bundle iife.');
|
console.log('Building web bundle iife.');
|
||||||
await build({
|
await build({
|
||||||
...buildConfig,
|
...buildConfig,
|
||||||
|
|||||||
@@ -89,6 +89,7 @@
|
|||||||
"safe-stable-stringify": "2.4.1",
|
"safe-stable-stringify": "2.4.1",
|
||||||
"stream-buffers": "^3.0.2",
|
"stream-buffers": "^3.0.2",
|
||||||
"unzipit": "^1.4.0",
|
"unzipit": "^1.4.0",
|
||||||
|
"warp-arbundles": "1.0.1",
|
||||||
"warp-isomorphic": "1.0.4",
|
"warp-isomorphic": "1.0.4",
|
||||||
"warp-wasm-metering": "1.0.1"
|
"warp-wasm-metering": "1.0.1"
|
||||||
},
|
},
|
||||||
@@ -117,8 +118,8 @@
|
|||||||
"ts-jest": "^28.0.7",
|
"ts-jest": "^28.0.7",
|
||||||
"ts-node": "^10.2.1",
|
"ts-node": "^10.2.1",
|
||||||
"typescript": "^4.9.5",
|
"typescript": "^4.9.5",
|
||||||
"warp-contracts-plugin-deploy": "1.0.8-beta.0",
|
"warp-contracts-plugin-deploy": "1.0.8",
|
||||||
"warp-contracts-plugin-vm2": "1.0.0",
|
"warp-contracts-plugin-vm2": "1.0.1",
|
||||||
"warp-contracts-plugin-vrf": "^1.0.3",
|
"warp-contracts-plugin-vrf": "^1.0.3",
|
||||||
"ws": "^8.11.0"
|
"ws": "^8.11.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -43,9 +43,7 @@ describe('Wallet', () => {
|
|||||||
it(`should throw for custom signing function and ethereum signature type`, () => {
|
it(`should throw for custom signing function and ethereum signature type`, () => {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
new Signature(warp, { signer: sampleFunction, type: 'ethereum' });
|
new Signature(warp, { signer: sampleFunction, type: 'ethereum' });
|
||||||
}).toThrow(
|
}).toThrow(`Unable to use signing function with signature of type: ethereum.`);
|
||||||
`Unable to use signing function of type: ethereum when not in mainnet environment or bundling is disabled.`
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -70,9 +68,18 @@ describe('Wallet', () => {
|
|||||||
expect(sut.type).toStrictEqual('arweave');
|
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`, () => {
|
it(`should set correct signature for custom signing function and arweave signature type`, () => {
|
||||||
const sut = new Signature(warp, { signer: sampleFunction, type: 'arweave' });
|
const sut = new Signature(warp, { signer: sampleFunction, type: 'arweave' });
|
||||||
|
|
||||||
expect(sut.signer).toEqual(sampleFunction);
|
expect(sut.signer).toEqual(sampleFunction);
|
||||||
expect(sut.type).toEqual('arweave');
|
expect(sut.type).toEqual('arweave');
|
||||||
});
|
});
|
||||||
@@ -104,12 +111,10 @@ describe('Wallet', () => {
|
|||||||
expect(sut.type).toEqual('arweave');
|
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(() => {
|
expect(() => {
|
||||||
const sut = new Signature(warp, { signer: sampleFunction, type: 'ethereum' });
|
const sut = new Signature(warp, { signer: sampleFunction, type: 'ethereum' });
|
||||||
}).toThrow(
|
}).toThrow(`Unable to use signing function with signature of type: ethereum.`);
|
||||||
`Unable to use signing function of type: ethereum when not in mainnet environment or bundling is disabled.`
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -132,16 +137,16 @@ describe('Wallet', () => {
|
|||||||
expect(sut.type).toStrictEqual('arweave');
|
expect(sut.type).toStrictEqual('arweave');
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should set correct signature for custom signing function and arweave signature type`, () => {
|
it(`should throw for custom signing function and ethereum signature type`, () => {
|
||||||
const sut = new Signature(warp, { signer: sampleFunction, type: 'arweave' });
|
expect(() => {
|
||||||
expect(sut.signer.toString().replace(/\s+/g, '')).toEqual(sampleFunction.toString().replace(/\s+/g, ''));
|
const sut = new Signature(warp, { signer: sampleFunction, type: 'ethereum' });
|
||||||
expect(sut.type).toEqual('arweave');
|
}).toThrow(`Unable to use signing function when bundling is enabled.`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should set correct signature for custom signing function and ethereum signature type`, () => {
|
it(`should throw for custom signing function and arweave signature type`, () => {
|
||||||
const sut = new Signature(warp, { signer: sampleFunction, type: 'ethereum' });
|
expect(() => {
|
||||||
expect(sut.signer.toString().replace(/\s+/g, '')).toEqual(sampleFunction.toString().replace(/\s+/g, ''));
|
const sut = new Signature(warp, { signer: sampleFunction, type: 'arweave' });
|
||||||
expect(sut.signer).toEqual(sampleFunction);
|
}).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 () => {
|
it('should call getAddress for customSignature, if getAddress provided', async () => {
|
||||||
const warp = WarpFactory.forMainnet();
|
const warp = WarpFactory.forMainnet(defaultCacheOptions, true);
|
||||||
const customSignature: CustomSignature = {
|
const customSignature: CustomSignature = {
|
||||||
type: 'ethereum',
|
type: 'arweave',
|
||||||
signer: sampleFunction,
|
signer: sampleFunction,
|
||||||
getAddress: () => Promise.resolve('owner')
|
getAddress: () => Promise.resolve('owner')
|
||||||
};
|
};
|
||||||
@@ -171,39 +176,36 @@ describe('Wallet', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should call getAddress for customSignature, if getAddress NOT provided', async () => {
|
it('should call getAddress for customSignature, if getAddress NOT provided', async () => {
|
||||||
const warp = WarpFactory.forMainnet();
|
const warp = WarpFactory.forMainnet(defaultCacheOptions, true);
|
||||||
const customSignature: CustomSignature = {
|
const customSignature: CustomSignature = {
|
||||||
type: 'ethereum',
|
type: 'arweave',
|
||||||
signer: async (tx) => {
|
signer: async (tx) => {
|
||||||
tx.owner = 'owner';
|
Promise.resolve(tx);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const signature = new Signature(warp, customSignature);
|
const signature = new Signature(warp, customSignature);
|
||||||
|
|
||||||
const address = await signature.getAddress();
|
const address = await signature.getAddress();
|
||||||
expect(address).toStrictEqual('owner');
|
expect(address).toStrictEqual('47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should use cached valued from getAddress', async () => {
|
it('should use cached valued from getAddress', async () => {
|
||||||
const warp = WarpFactory.forMainnet();
|
const warp = WarpFactory.forMainnet(defaultCacheOptions, true);
|
||||||
const mockedSigner = jest.fn(async (tx) => {
|
|
||||||
tx.owner = 'owner';
|
|
||||||
});
|
|
||||||
const customSignature: CustomSignature = {
|
const customSignature: CustomSignature = {
|
||||||
type: 'ethereum',
|
type: 'arweave',
|
||||||
signer: mockedSigner
|
signer: async (tx) => {
|
||||||
|
Promise.resolve(tx);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const signature = new Signature(warp, customSignature);
|
const signature = new Signature(warp, customSignature);
|
||||||
|
|
||||||
const address = await signature.getAddress();
|
const address = await signature.getAddress();
|
||||||
expect(address).toStrictEqual('owner');
|
expect(address).toStrictEqual('47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU');
|
||||||
|
|
||||||
const cachedAddress = await signature.getAddress();
|
const cachedAddress = await signature.getAddress();
|
||||||
expect(cachedAddress).toStrictEqual('owner');
|
expect(cachedAddress).toStrictEqual('47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU');
|
||||||
|
|
||||||
expect(mockedSigner).toBeCalledTimes(1);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { ArTransfer, Tags, ArWallet } from './deploy/CreateContract';
|
|||||||
import { CustomSignature } from './Signature';
|
import { CustomSignature } from './Signature';
|
||||||
import { EvaluationOptionsEvaluator } from './EvaluationOptionsEvaluator';
|
import { EvaluationOptionsEvaluator } from './EvaluationOptionsEvaluator';
|
||||||
import { InteractionState } from './states/InteractionState';
|
import { InteractionState } from './states/InteractionState';
|
||||||
|
import { Signer } from 'warp-arbundles';
|
||||||
|
|
||||||
export type BenchmarkStats = { gatewayCommunication: number; stateEvaluation: number; total: number };
|
export type BenchmarkStats = { gatewayCommunication: number; stateEvaluation: number; total: number };
|
||||||
|
|
||||||
@@ -87,9 +88,9 @@ export interface Contract<State = unknown> {
|
|||||||
* i.e. whether called contract's function required "caller" info)
|
* i.e. whether called contract's function required "caller" info)
|
||||||
* Connecting a signer MUST be done before "writeInteraction" and "bundleInteraction".
|
* 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<State>;
|
connect(signature: ArWallet | CustomSignature | Signer): Contract<State>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows to set ({@link EvaluationOptions})
|
* Allows to set ({@link EvaluationOptions})
|
||||||
|
|||||||
@@ -14,13 +14,13 @@ import { InteractionsSorter } from '../core/modules/InteractionsSorter';
|
|||||||
import { DefaultEvaluationOptions, EvalStateResult, EvaluationOptions } from '../core/modules/StateEvaluator';
|
import { DefaultEvaluationOptions, EvalStateResult, EvaluationOptions } from '../core/modules/StateEvaluator';
|
||||||
import { WARP_TAGS } from '../core/KnownTags';
|
import { WARP_TAGS } from '../core/KnownTags';
|
||||||
import { Warp } from '../core/Warp';
|
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 { GQLNodeInterface } from '../legacy/gqlResult';
|
||||||
import { Benchmark } from '../logging/Benchmark';
|
import { Benchmark } from '../logging/Benchmark';
|
||||||
import { LoggerFactory } from '../logging/LoggerFactory';
|
import { LoggerFactory } from '../logging/LoggerFactory';
|
||||||
import { Evolve } from '../plugins/Evolve';
|
import { Evolve } from '../plugins/Evolve';
|
||||||
import { ArweaveWrapper } from '../utils/ArweaveWrapper';
|
import { ArweaveWrapper } from '../utils/ArweaveWrapper';
|
||||||
import { getJsonResponse, sleep, stripTrailingSlash } from '../utils/utils';
|
import { getJsonResponse, isBrowser, sleep, stripTrailingSlash } from '../utils/utils';
|
||||||
import {
|
import {
|
||||||
BenchmarkStats,
|
BenchmarkStats,
|
||||||
Contract,
|
Contract,
|
||||||
@@ -35,12 +35,13 @@ import { CustomSignature, Signature } from './Signature';
|
|||||||
import { EvaluationOptionsEvaluator } from './EvaluationOptionsEvaluator';
|
import { EvaluationOptionsEvaluator } from './EvaluationOptionsEvaluator';
|
||||||
import { WarpFetchWrapper } from '../core/WarpFetchWrapper';
|
import { WarpFetchWrapper } from '../core/WarpFetchWrapper';
|
||||||
import { Mutex } from 'async-mutex';
|
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 { InteractionState } from './states/InteractionState';
|
||||||
import { ContractInteractionState } from './states/ContractInteractionState';
|
import { ContractInteractionState } from './states/ContractInteractionState';
|
||||||
import { Crypto } from 'warp-isomorphic';
|
import { Crypto } from 'warp-isomorphic';
|
||||||
import { VrfPluginFunctions } from '../core/WarpPlugin';
|
import { VrfPluginFunctions } from '../core/WarpPlugin';
|
||||||
import Arweave from 'arweave';
|
import Arweave from 'arweave';
|
||||||
|
import { createData, tagsExceedLimit, DataItem, Signer } from 'warp-arbundles';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An implementation of {@link Contract} that is backwards compatible with current style
|
* 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.
|
* It requires {@link ExecutorFactory} that is using {@link HandlerApi} generic type.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export class HandlerBasedContract<State> implements Contract<State> {
|
export class HandlerBasedContract<State> implements Contract<State> {
|
||||||
private readonly logger = LoggerFactory.INST.create('HandlerBasedContract');
|
private readonly logger = LoggerFactory.INST.create('HandlerBasedContract');
|
||||||
|
|
||||||
@@ -261,6 +263,7 @@ export class HandlerBasedContract<State> implements Contract<State> {
|
|||||||
const bundleInteraction = interactionsLoader.type() == 'warp' && !effectiveDisableBundling;
|
const bundleInteraction = interactionsLoader.type() == 'warp' && !effectiveDisableBundling;
|
||||||
|
|
||||||
this._signature.checkNonArweaveSigningAvailability(bundleInteraction);
|
this._signature.checkNonArweaveSigningAvailability(bundleInteraction);
|
||||||
|
this._signature.checkBundlerSignerAvailability(bundleInteraction);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
bundleInteraction &&
|
bundleInteraction &&
|
||||||
@@ -274,6 +277,10 @@ export class HandlerBasedContract<State> implements Contract<State> {
|
|||||||
throw new Error('Vrf generation is only available for bundle interaction');
|
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) {
|
if (bundleInteraction) {
|
||||||
return await this.bundleInteraction(input, {
|
return await this.bundleInteraction(input, {
|
||||||
tags: effectiveTags,
|
tags: effectiveTags,
|
||||||
@@ -322,34 +329,86 @@ export class HandlerBasedContract<State> implements Contract<State> {
|
|||||||
): Promise<WriteInteractionResponse | null> {
|
): Promise<WriteInteractionResponse | null> {
|
||||||
this.logger.info('Bundle interaction input', input);
|
this.logger.info('Bundle interaction input', input);
|
||||||
|
|
||||||
const interactionTx = await this.createInteraction(
|
const interactionDataItem = await this.createInteractionDataItem(
|
||||||
input,
|
input,
|
||||||
options.tags,
|
options.tags,
|
||||||
emptyTransfer,
|
emptyTransfer,
|
||||||
options.strict,
|
options.strict,
|
||||||
true,
|
|
||||||
options.vrf
|
options.vrf
|
||||||
);
|
);
|
||||||
|
|
||||||
const response = this._warpFetchWrapper.fetch(
|
const response = this._warpFetchWrapper.fetch(
|
||||||
`${stripTrailingSlash(this._evaluationOptions.sequencerUrl)}/gateway/sequencer/register`,
|
`${stripTrailingSlash(this._evaluationOptions.sequencerUrl)}/gateway/v2/sequencer/register`,
|
||||||
{
|
{
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify(interactionTx),
|
|
||||||
headers: {
|
headers: {
|
||||||
'Accept-Encoding': 'gzip, deflate, br',
|
'Content-Type': 'application/octet-stream',
|
||||||
'Content-Type': 'application/json',
|
|
||||||
Accept: 'application/json'
|
Accept: 'application/json'
|
||||||
}
|
},
|
||||||
|
body: interactionDataItem.getRaw()
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const dataItemId = await interactionDataItem.id;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
bundlrResponse: await getJsonResponse(response),
|
bundlrResponse: await getJsonResponse(response),
|
||||||
originalTxId: interactionTx.id
|
originalTxId: dataItemId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async createInteractionDataItem<Input>(
|
||||||
|
input: Input,
|
||||||
|
tags: Tags,
|
||||||
|
transfer: ArTransfer,
|
||||||
|
strict: boolean,
|
||||||
|
vrf = false
|
||||||
|
) {
|
||||||
|
if (this._evaluationOptions.internalWrites) {
|
||||||
|
// it modifies tags
|
||||||
|
await this.discoverInternalWrites<Input>(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>(
|
private async createInteraction<Input>(
|
||||||
input: Input,
|
input: Input,
|
||||||
tags: Tags,
|
tags: Tags,
|
||||||
@@ -365,10 +424,7 @@ export class HandlerBasedContract<State> implements Contract<State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (vrf) {
|
if (vrf) {
|
||||||
tags.push({
|
tags.push(new Tag(WARP_TAGS.REQUEST_VRF, 'true'));
|
||||||
name: WARP_TAGS.REQUEST_VRF,
|
|
||||||
value: 'true'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const interactionTx = await createInteractionTx(
|
const interactionTx = await createInteractionTx(
|
||||||
@@ -385,20 +441,28 @@ export class HandlerBasedContract<State> implements Contract<State> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!this._evaluationOptions.internalWrites && strict) {
|
if (!this._evaluationOptions.internalWrites && strict) {
|
||||||
const { arweave } = this.warp;
|
await this.checkInteractionInStrictMode(interactionTx.owner, input, tags, transfer, strict, vrf);
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return interactionTx;
|
return interactionTx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async checkInteractionInStrictMode<Input>(
|
||||||
|
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 {
|
txId(): string {
|
||||||
return this._contractTxId;
|
return this._contractTxId;
|
||||||
}
|
}
|
||||||
@@ -407,7 +471,7 @@ export class HandlerBasedContract<State> implements Contract<State> {
|
|||||||
return this._callStack;
|
return this._callStack;
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(signature: ArWallet | CustomSignature): Contract<State> {
|
connect(signature: ArWallet | CustomSignature | Signer): Contract<State> {
|
||||||
this._signature = new Signature(this.warp, signature);
|
this._signature = new Signature(this.warp, signature);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -975,10 +1039,7 @@ export class HandlerBasedContract<State> implements Contract<State> {
|
|||||||
this.logger.debug('Callstack', callStack.print());
|
this.logger.debug('Callstack', callStack.print());
|
||||||
|
|
||||||
innerWrites.forEach((contractTxId) => {
|
innerWrites.forEach((contractTxId) => {
|
||||||
tags.push({
|
tags.push(new Tag(WARP_TAGS.INTERACT_WRITE, contractTxId));
|
||||||
name: WARP_TAGS.INTERACT_WRITE,
|
|
||||||
value: contractTxId
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.logger.debug('Tags with inner calls', tags);
|
this.logger.debug('Tags with inner calls', tags);
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import { Warp } from '../core/Warp';
|
import { Warp } from '../core/Warp';
|
||||||
import { ArWallet } from './deploy/CreateContract';
|
import { ArWallet } from './deploy/CreateContract';
|
||||||
import { Transaction } from '../utils/types/arweave-types';
|
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 SignatureType = 'arweave' | 'ethereum';
|
||||||
export type SigningFunction = (tx: Transaction) => Promise<void>;
|
export type SigningFunction = (tx: Transaction) => Promise<void>;
|
||||||
@@ -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'
|
- 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
|
- 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'
|
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
|
- Signer - arbundles specific class which allows to sign data items (works with data items - when bundling is enabled)
|
||||||
are being created)
|
|
||||||
*/
|
*/
|
||||||
export type SignatureProvider = ArWallet | CustomSignature | BundlerSigner;
|
export type SignatureProvider = ArWallet | CustomSignature | BundlerSigner;
|
||||||
|
|
||||||
export class Signature {
|
export class Signature {
|
||||||
signer: SigningFunction;
|
signer: SigningFunction;
|
||||||
|
bundlerSigner: BundlerSigner;
|
||||||
readonly type: SignatureType;
|
readonly type: SignatureType;
|
||||||
readonly warp: Warp;
|
readonly warp: Warp;
|
||||||
private readonly signatureProviderType: 'CustomSignature' | 'ArWallet' | 'BundlerSigner';
|
private readonly signatureProviderType: 'CustomSignature' | 'ArWallet' | 'BundlerSigner';
|
||||||
@@ -40,8 +42,11 @@ export class Signature {
|
|||||||
} else if (this.isValidBundlerSignature(walletOrSignature)) {
|
} else if (this.isValidBundlerSignature(walletOrSignature)) {
|
||||||
this.signatureProviderType = 'BundlerSigner';
|
this.signatureProviderType = 'BundlerSigner';
|
||||||
this.type = decodeBundleSignatureType(walletOrSignature.signatureType);
|
this.type = decodeBundleSignatureType(walletOrSignature.signatureType);
|
||||||
|
this.bundlerSigner = walletOrSignature;
|
||||||
} else {
|
} else {
|
||||||
this.assignArweaveSigner(walletOrSignature);
|
this.assignArweaveSigner(walletOrSignature);
|
||||||
|
this.bundlerSigner =
|
||||||
|
typeof walletOrSignature == 'string' ? null : new ArweaveSigner(walletOrSignature as JWKInterface);
|
||||||
this.signatureProviderType = 'ArWallet';
|
this.signatureProviderType = 'ArWallet';
|
||||||
this.type = 'arweave';
|
this.type = 'arweave';
|
||||||
}
|
}
|
||||||
@@ -76,19 +81,22 @@ export class Signature {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async deduceSignerBySigning() {
|
private async deduceSignerBySigning(): Promise<string> {
|
||||||
const { arweave } = this.warp;
|
const { arweave } = this.warp;
|
||||||
|
|
||||||
const dummyTx = await arweave.createTransaction({
|
if (this.signatureProviderType == 'BundlerSigner') {
|
||||||
data: Math.random().toString().slice(-4),
|
try {
|
||||||
reward: '72600854',
|
return await this.bundlerSigner.getAddress();
|
||||||
last_tx: 'p7vc1iSP6bvH_fCeUFa9LqoV5qiyW-jdEKouAT0XMoSwrNraB9mgpi29Q10waEpO'
|
} catch (e) {
|
||||||
});
|
throw new Error(`Could not get address from the signer. Is the 'getAddress' implementation correct?'`);
|
||||||
await this.signer(dummyTx);
|
}
|
||||||
|
} else if (this.signatureProviderType == 'ArWallet' || this.signatureProviderType == 'CustomSignature') {
|
||||||
if (this.type === 'ethereum') {
|
const dummyTx = await arweave.createTransaction({
|
||||||
return dummyTx.owner;
|
data: Math.random().toString().slice(-4),
|
||||||
} else if (this.type === 'arweave') {
|
reward: '72600854',
|
||||||
|
last_tx: 'p7vc1iSP6bvH_fCeUFa9LqoV5qiyW-jdEKouAT0XMoSwrNraB9mgpi29Q10waEpO'
|
||||||
|
});
|
||||||
|
await (this.signer as SigningFunction)(dummyTx);
|
||||||
return arweave.wallets.ownerToAddress(dummyTx.owner);
|
return arweave.wallets.ownerToAddress(dummyTx.owner);
|
||||||
} else {
|
} else {
|
||||||
throw Error('Unknown Signature::type');
|
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) {
|
private assignArweaveSigner(walletOrSignature) {
|
||||||
this.signer = async (tx: Transaction) => {
|
this.signer = async (tx: Transaction) => {
|
||||||
await this.warp.arweave.transactions.sign(tx, walletOrSignature);
|
await this.warp.arweave.transactions.sign(tx, walletOrSignature);
|
||||||
@@ -108,19 +128,17 @@ export class Signature {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private assertEnvForCustomSigner(signatureType: SignatureType) {
|
private assertEnvForCustomSigner(signatureType: SignatureType) {
|
||||||
if (signatureType === 'arweave') {
|
if (this.warp.interactionsLoader.type() === 'warp') {
|
||||||
return;
|
throw new Error(`Unable to use signing function when bundling is enabled.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!['mainnet', 'testnet'].includes(this.warp.environment) || this.warp.interactionsLoader.type() !== 'warp') {
|
if (signatureType == 'ethereum') {
|
||||||
throw new Error(
|
throw new Error(`Unable to use signing function with signature of type: ${signatureType}.`);
|
||||||
`Unable to use signing function of type: ${signatureType} when not in mainnet environment or bundling is disabled.`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private isCustomSignature(signature: SignatureProvider): signature is CustomSignature {
|
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 {
|
private isValidBundlerSignature(signature: SignatureProvider): signature is BundlerSigner {
|
||||||
|
|||||||
@@ -4,8 +4,9 @@ import { EvaluationOptions } from '../../core/modules/StateEvaluator';
|
|||||||
import { Source } from './Source';
|
import { Source } from './Source';
|
||||||
import { BundlerSigner } from './DataItem';
|
import { BundlerSigner } from './DataItem';
|
||||||
import { CustomSignature } from '../Signature';
|
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';
|
export type ArWallet = JWKInterface | 'use_wallet';
|
||||||
|
|
||||||
|
|||||||
@@ -3,14 +3,15 @@ import { SMART_WEAVE_TAGS, WARP_TAGS } from '../core/KnownTags';
|
|||||||
import { GQLNodeInterface } from './gqlResult';
|
import { GQLNodeInterface } from './gqlResult';
|
||||||
import { TagsParser } from '../core/modules/impl/TagsParser';
|
import { TagsParser } from '../core/modules/impl/TagsParser';
|
||||||
import { SigningFunction } from '../contract/Signature';
|
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<Input>(
|
export async function createInteractionTx<Input>(
|
||||||
arweave: Arweave,
|
arweave: Arweave,
|
||||||
signer: SigningFunction,
|
signer: SigningFunction,
|
||||||
contractId: string,
|
contractId: string,
|
||||||
input: Input,
|
input: Input,
|
||||||
tags: { name: string; value: string }[],
|
tags: Tags,
|
||||||
target = '',
|
target = '',
|
||||||
winstonQty = '0',
|
winstonQty = '0',
|
||||||
dummy = false,
|
dummy = false,
|
||||||
@@ -42,24 +43,8 @@ export async function createInteractionTx<Input>(
|
|||||||
|
|
||||||
const interactionTx = await arweave.createTransaction(options);
|
const interactionTx = await arweave.createTransaction(options);
|
||||||
|
|
||||||
if (!input) {
|
const interactionTags = createInteractionTagsList(contractId, input, isTestnet, tags);
|
||||||
throw new Error(`Input should be a truthy value: ${JSON.stringify(input)}`);
|
interactionTags.forEach((t) => interactionTx.addTag(t.name, t.value));
|
||||||
}
|
|
||||||
|
|
||||||
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');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (signer) {
|
if (signer) {
|
||||||
await signer(interactionTx);
|
await signer(interactionTx);
|
||||||
@@ -114,3 +99,30 @@ export function createDummyTx(tx: Transaction, from: string, block: BlockData):
|
|||||||
bundledIn: null
|
bundledIn: null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createInteractionTagsList<Input>(
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|||||||
30
yarn.lock
30
yarn.lock
@@ -2365,7 +2365,7 @@ arweave-stream-tx@^1.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
exponential-backoff "^3.1.0"
|
exponential-backoff "^3.1.0"
|
||||||
|
|
||||||
arweave@1.13.7:
|
arweave@1.13.7, arweave@^1.13.7:
|
||||||
version "1.13.7"
|
version "1.13.7"
|
||||||
resolved "https://registry.yarnpkg.com/arweave/-/arweave-1.13.7.tgz#cda8534c833baec372a7052c61f53b4e39a886d7"
|
resolved "https://registry.yarnpkg.com/arweave/-/arweave-1.13.7.tgz#cda8534c833baec372a7052c61f53b4e39a886d7"
|
||||||
integrity sha512-Hv+x2bSI6UyBHpuVbUDMMpMje1ETfpJWj52kKfz44O0IqDRi/LukOkkDUptup1p6OT6KP1/DdpnUnsNHoskFeA==
|
integrity sha512-Hv+x2bSI6UyBHpuVbUDMMpMje1ETfpJWj52kKfz44O0IqDRi/LukOkkDUptup1p6OT6KP1/DdpnUnsNHoskFeA==
|
||||||
@@ -7472,19 +7472,29 @@ walker@^1.0.8:
|
|||||||
dependencies:
|
dependencies:
|
||||||
makeerror "1.0.12"
|
makeerror "1.0.12"
|
||||||
|
|
||||||
warp-contracts-plugin-deploy@1.0.8-beta.0:
|
warp-arbundles@1.0.1:
|
||||||
version "1.0.8-beta.0"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/warp-contracts-plugin-deploy/-/warp-contracts-plugin-deploy-1.0.8-beta.0.tgz#c4c95a57d53a5ae2a50c0907f94ac74e6bcdbada"
|
resolved "https://registry.yarnpkg.com/warp-arbundles/-/warp-arbundles-1.0.1.tgz#c816ee44aacd90e0214b1b7db8028c6d5b468770"
|
||||||
integrity sha512-bA8K9QHuIvE1XzWpFYmAR5+xisanAzfInxx0ekMwlGVFiDWciWcVAFNefLUa5iFHuHlDpqgDQvUpsd6lg7Wx8g==
|
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:
|
dependencies:
|
||||||
arbundles "^0.7.3"
|
arbundles "^0.7.3"
|
||||||
arlocal "^1.1.59"
|
arlocal "^1.1.59"
|
||||||
node-stdlib-browser "^1.2.0"
|
node-stdlib-browser "^1.2.0"
|
||||||
|
|
||||||
warp-contracts-plugin-vm2@1.0.0:
|
warp-contracts-plugin-vm2@1.0.1:
|
||||||
version "1.0.0"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/warp-contracts-plugin-vm2/-/warp-contracts-plugin-vm2-1.0.0.tgz#f55914a228102f3bda7dafc197c395078fa0e190"
|
resolved "https://registry.yarnpkg.com/warp-contracts-plugin-vm2/-/warp-contracts-plugin-vm2-1.0.1.tgz#0c1749f998508e8559a4627afc574ff80577353c"
|
||||||
integrity sha512-3XubaZ9eeRbBtMDwmTAewZk6WYgtcg9g44vATqH5vmn2wdPWIBcrBX3gA7XQIDbqPcLPpIgifMDZiQJSNwYZDA==
|
integrity sha512-ogECglCyZZ3vF7nm9RqkDA+b8AwHmoda8Uv2oYIMyxm7haLjTMtZNY+QvqDbI3b/B/MR+nX1nwdTe7KIUqqQjQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
bignumber.js "^9.1.1"
|
bignumber.js "^9.1.1"
|
||||||
vm2 "^3.9.16"
|
vm2 "^3.9.16"
|
||||||
@@ -7505,7 +7515,7 @@ warp-isomorphic@1.0.0:
|
|||||||
buffer "^6.0.3"
|
buffer "^6.0.3"
|
||||||
undici "^5.8.0"
|
undici "^5.8.0"
|
||||||
|
|
||||||
warp-isomorphic@1.0.4:
|
warp-isomorphic@1.0.4, warp-isomorphic@^1.0.4:
|
||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/warp-isomorphic/-/warp-isomorphic-1.0.4.tgz#1017eba260e0f0228b33b94b9e36a1afe54e09d8"
|
resolved "https://registry.yarnpkg.com/warp-isomorphic/-/warp-isomorphic-1.0.4.tgz#1017eba260e0f0228b33b94b9e36a1afe54e09d8"
|
||||||
integrity sha512-W77IoLjq/eu5bY1uRrlmVt5lLDoIHeZ0ozJ/j67FTnxvZRXu887biEnom1nx8q1UgOKyJh8eQYFQaE2FLlKhFg==
|
integrity sha512-W77IoLjq/eu5bY1uRrlmVt5lLDoIHeZ0ozJ/j67FTnxvZRXu887biEnom1nx8q1UgOKyJh8eQYFQaE2FLlKhFg==
|
||||||
|
|||||||
Reference in New Issue
Block a user