feat: save source through bundlr (#283)
This commit is contained in:
@@ -120,7 +120,8 @@ describe('Testing the Profit Sharing Token', () => {
|
|||||||
|
|
||||||
const newSource = fs.readFileSync(path.join(__dirname, '../data/token-evolve.js'), 'utf8');
|
const newSource = fs.readFileSync(path.join(__dirname, '../data/token-evolve.js'), 'utf8');
|
||||||
|
|
||||||
const newSrcTxId = await pst.save({ src: newSource }, warp.environment);
|
const srcTx = await warp.createSourceTx({ src: newSource }, wallet);
|
||||||
|
const newSrcTxId = await warp.saveSourceTx(srcTx);
|
||||||
await mineBlock(warp);
|
await mineBlock(warp);
|
||||||
|
|
||||||
await pst.evolve(newSrcTxId);
|
await pst.evolve(newSrcTxId);
|
||||||
|
|||||||
@@ -159,13 +159,15 @@ describe('Testing the Warp client for AssemblyScript WASM contract', () => {
|
|||||||
|
|
||||||
const newContractSrc = fs.readFileSync(path.join(__dirname, '../data/wasm/as/assemblyscript-counter-evolve.wasm'));
|
const newContractSrc = fs.readFileSync(path.join(__dirname, '../data/wasm/as/assemblyscript-counter-evolve.wasm'));
|
||||||
|
|
||||||
const newSrcTxId = await contract.save(
|
const srcTx = await warp.createSourceTx(
|
||||||
{
|
{
|
||||||
src: newContractSrc,
|
src: newContractSrc,
|
||||||
wasmSrcCodeDir: path.join(__dirname, '../data/wasm/as/assembly-evolve')
|
wasmSrcCodeDir: path.join(__dirname, '../data/wasm/as/assembly-evolve')
|
||||||
},
|
},
|
||||||
warp.environment
|
wallet
|
||||||
);
|
);
|
||||||
|
const newSrcTxId = await warp.saveSourceTx(srcTx);
|
||||||
|
|
||||||
await mineBlock(warp);
|
await mineBlock(warp);
|
||||||
|
|
||||||
await contract.evolve(newSrcTxId);
|
await contract.evolve(newSrcTxId);
|
||||||
|
|||||||
@@ -197,13 +197,14 @@ describe('Testing the Go WASM Profit Sharing Token', () => {
|
|||||||
|
|
||||||
const newContractSrc = fs.readFileSync(path.join(__dirname, '../data/wasm/go/go-pst-evolve.wasm'));
|
const newContractSrc = fs.readFileSync(path.join(__dirname, '../data/wasm/go/go-pst-evolve.wasm'));
|
||||||
|
|
||||||
const newSrcTxId = await pst.save(
|
const srcTx = await warp.createSourceTx(
|
||||||
{
|
{
|
||||||
src: newContractSrc,
|
src: newContractSrc,
|
||||||
wasmSrcCodeDir: path.join(__dirname, '../data/wasm/go/src-evolve')
|
wasmSrcCodeDir: path.join(__dirname, '../data/wasm/go/src-evolve')
|
||||||
},
|
},
|
||||||
warp.environment
|
wallet
|
||||||
);
|
);
|
||||||
|
const newSrcTxId = await warp.saveSourceTx(srcTx);
|
||||||
|
|
||||||
await mineBlock(warp);
|
await mineBlock(warp);
|
||||||
|
|
||||||
|
|||||||
@@ -223,14 +223,15 @@ describe('Testing the Rust WASM Profit Sharing Token', () => {
|
|||||||
|
|
||||||
const newContractSrc = fs.readFileSync(path.join(__dirname, '../data/wasm/rust/rust-pst-evolve_bg.wasm'));
|
const newContractSrc = fs.readFileSync(path.join(__dirname, '../data/wasm/rust/rust-pst-evolve_bg.wasm'));
|
||||||
|
|
||||||
const newSrcTxId = await pst.save(
|
const srcTx = await warp.createSourceTx(
|
||||||
{
|
{
|
||||||
src: newContractSrc,
|
src: newContractSrc,
|
||||||
wasmSrcCodeDir: path.join(__dirname, '../data/wasm/rust/src-evolve'),
|
wasmSrcCodeDir: path.join(__dirname, '../data/wasm/rust/src-evolve'),
|
||||||
wasmGlueCode: path.join(__dirname, '../data/wasm/rust/rust-pst-evolve.js')
|
wasmGlueCode: path.join(__dirname, '../data/wasm/rust/rust-pst-evolve.js')
|
||||||
},
|
},
|
||||||
warp.environment
|
wallet
|
||||||
);
|
);
|
||||||
|
const newSrcTxId = await warp.saveSourceTx(srcTx);
|
||||||
|
|
||||||
await mineBlock(warp);
|
await mineBlock(warp);
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { InteractionResult } from '../core/modules/impl/HandlerExecutorFactory';
|
|||||||
import { EvaluationOptions, EvalStateResult } from '../core/modules/StateEvaluator';
|
import { EvaluationOptions, EvalStateResult } from '../core/modules/StateEvaluator';
|
||||||
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 { SignatureType } from './Signature';
|
import { SignatureType } from './Signature';
|
||||||
|
|
||||||
export type CurrentTx = { interactionTxId: string; contractTxId: string };
|
export type CurrentTx = { interactionTxId: string; contractTxId: string };
|
||||||
@@ -69,7 +68,7 @@ export type InnerCallData = { callingInteraction: GQLNodeInterface; callType: In
|
|||||||
* A base interface to be implemented by SmartWeave Contracts clients
|
* A base interface to be implemented by SmartWeave Contracts clients
|
||||||
* - contains "low-level" methods that allow to interact with any contract
|
* - contains "low-level" methods that allow to interact with any contract
|
||||||
*/
|
*/
|
||||||
export interface Contract<State = unknown> extends Source {
|
export interface Contract<State = unknown> {
|
||||||
/**
|
/**
|
||||||
* Returns the Arweave transaction id of this contract.
|
* Returns the Arweave transaction id of this contract.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -754,17 +754,6 @@ export class HandlerBasedContract<State> implements Contract<State> {
|
|||||||
return await this.writeInteraction<any>({ function: 'evolve', value: newSrcTxId }, options);
|
return await this.writeInteraction<any>({ function: 'evolve', value: newSrcTxId }, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
async save(sourceData: SourceData): Promise<any> {
|
|
||||||
if (!this.signature) {
|
|
||||||
throw new Error("Wallet not connected. Use 'connect' method first.");
|
|
||||||
}
|
|
||||||
const source = new SourceImpl(this.warp);
|
|
||||||
|
|
||||||
const srcTx = await source.save(sourceData, this.warp.environment, this.signature);
|
|
||||||
|
|
||||||
return srcTx.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
get rootSortKey(): string {
|
get rootSortKey(): string {
|
||||||
return this._rootSortKey;
|
return this._rootSortKey;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { JWKInterface } from 'arweave/node/lib/wallet';
|
import { JWKInterface } from 'arweave/node/lib/wallet';
|
||||||
import { SignatureType } from '../../contract/Signature';
|
import { SignatureType } from '../../contract/Signature';
|
||||||
|
import { Source } from './Source';
|
||||||
|
|
||||||
export type Tags = { name: string; value: string }[];
|
export type Tags = { name: string; value: string }[];
|
||||||
|
|
||||||
@@ -43,7 +44,7 @@ export interface ContractDeploy {
|
|||||||
srcTxId?: string;
|
srcTxId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateContract {
|
export interface CreateContract extends Source {
|
||||||
deploy(contractData: ContractData, disableBundling?: boolean): Promise<ContractDeploy>;
|
deploy(contractData: ContractData, disableBundling?: boolean): Promise<ContractDeploy>;
|
||||||
|
|
||||||
deployFromSourceTx(contractData: FromSrcTxContractData, disableBundling?: boolean): Promise<ContractDeploy>;
|
deployFromSourceTx(contractData: FromSrcTxContractData, disableBundling?: boolean): Promise<ContractDeploy>;
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
import { ArWallet } from './CreateContract';
|
import { ArWallet } from './CreateContract';
|
||||||
import { SourceData } from './impl/SourceImpl';
|
import { SourceData } from './impl/SourceImpl';
|
||||||
import { WarpEnvironment } from '../../core/Warp';
|
|
||||||
import { SignatureType } from '../../contract/Signature';
|
import { SignatureType } from '../../contract/Signature';
|
||||||
|
import Transaction from 'arweave/node/lib/transaction';
|
||||||
export interface Source {
|
export interface Source {
|
||||||
/**
|
/**
|
||||||
* allows to post contract source on Arweave
|
* allows to create contract source
|
||||||
* @param contractSource - contract source...
|
* @param sourceData - contract source data
|
||||||
|
* @param wallet - either Arweave wallet or custom signature type
|
||||||
*/
|
*/
|
||||||
save(
|
createSourceTx(sourceData: SourceData, wallet: ArWallet | SignatureType): Promise<Transaction>;
|
||||||
contractSource: SourceData,
|
|
||||||
env: WarpEnvironment,
|
/**
|
||||||
signer?: ArWallet | SignatureType,
|
* allows to save contract source
|
||||||
useBundler?: boolean
|
* @param sourceTx - contract source transaction
|
||||||
): Promise<string | null>;
|
* @param disableBundling = whether source should be deployed through bundlr using Warp Gateway
|
||||||
|
*/
|
||||||
|
saveSourceTx(sourceTx: Transaction, disableBundling?: boolean): Promise<string>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,24 @@
|
|||||||
/* 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 { Signature, SignatureType } 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';
|
||||||
import { LoggerFactory } from '../../../logging/LoggerFactory';
|
import { LoggerFactory } from '../../../logging/LoggerFactory';
|
||||||
import { CreateContract, ContractData, ContractDeploy, FromSrcTxContractData } from '../CreateContract';
|
import { CreateContract, ContractData, ContractDeploy, FromSrcTxContractData, ArWallet } from '../CreateContract';
|
||||||
import { SourceImpl } from './SourceImpl';
|
import { SourceData, SourceImpl } from './SourceImpl';
|
||||||
import { Buffer } from 'redstone-isomorphic';
|
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 readonly source: SourceImpl;
|
||||||
|
|
||||||
private signature: Signature;
|
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);
|
||||||
|
this.source = new SourceImpl(this.warp);
|
||||||
}
|
}
|
||||||
|
|
||||||
async deploy(contractData: ContractData, disableBundling?: boolean): Promise<ContractDeploy> {
|
async deploy(contractData: ContractData, disableBundling?: boolean): Promise<ContractDeploy> {
|
||||||
@@ -24,9 +27,11 @@ 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.warp);
|
const srcTx = await this.source.createSourceTx(contractData, wallet);
|
||||||
|
if (!effectiveUseBundler) {
|
||||||
|
await this.source.saveSourceTx(srcTx, true);
|
||||||
|
}
|
||||||
|
|
||||||
const srcTx = await source.save(contractData, this.warp.environment, wallet, effectiveUseBundler);
|
|
||||||
this.logger.debug('Creating new contract');
|
this.logger.debug('Creating new contract');
|
||||||
|
|
||||||
return await this.deployFromSourceTx(
|
return await this.deployFromSourceTx(
|
||||||
@@ -94,7 +99,7 @@ export class DefaultCreateContract implements CreateContract {
|
|||||||
let responseOk: boolean;
|
let responseOk: boolean;
|
||||||
let response: { status: number; statusText: string; data: any };
|
let response: { status: number; statusText: string; data: any };
|
||||||
if (effectiveUseBundler) {
|
if (effectiveUseBundler) {
|
||||||
const result = await this.post(contractTX, srcTx);
|
const result = await this.postContract(contractTX, srcTx);
|
||||||
this.logger.debug(result);
|
this.logger.debug(result);
|
||||||
responseOk = true;
|
responseOk = true;
|
||||||
} else {
|
} else {
|
||||||
@@ -136,7 +141,15 @@ export class DefaultCreateContract implements CreateContract {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async post(contractTx: Transaction, srcTx: Transaction = null): Promise<any> {
|
async createSourceTx(sourceData: SourceData, wallet: ArWallet | SignatureType): Promise<Transaction> {
|
||||||
|
return this.source.createSourceTx(sourceData, wallet);
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveSourceTx(srcTx: Transaction, disableBundling?: boolean): Promise<string> {
|
||||||
|
return this.source.saveSourceTx(srcTx, disableBundling);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async postContract(contractTx: Transaction, srcTx: Transaction = null): Promise<any> {
|
||||||
let body: any = {
|
let body: any = {
|
||||||
contractTx
|
contractTx
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,8 +8,11 @@ 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 { Warp, WarpEnvironment } from '../../../core/Warp';
|
import { Warp } from '../../../core/Warp';
|
||||||
import { Signature, SignatureType } from '../../../contract/Signature';
|
import { Signature, SignatureType } from '../../../contract/Signature';
|
||||||
|
import Transaction from 'arweave/node/lib/transaction';
|
||||||
|
import { WARP_GW_URL } from '../../../core/WarpFactory';
|
||||||
|
import { TagsParser } from '../../../core/modules/impl/TagsParser';
|
||||||
|
|
||||||
const wasmTypeMapping: Map<number, string> = new Map([
|
const wasmTypeMapping: Map<number, string> = new Map([
|
||||||
[1, 'assemblyscript'],
|
[1, 'assemblyscript'],
|
||||||
@@ -31,21 +34,14 @@ export class SourceImpl implements Source {
|
|||||||
|
|
||||||
constructor(private readonly warp: Warp) {}
|
constructor(private readonly warp: Warp) {}
|
||||||
|
|
||||||
async save(
|
async createSourceTx(sourceData: SourceData, wallet: ArWallet | SignatureType): Promise<Transaction> {
|
||||||
contractData: SourceData,
|
|
||||||
env: WarpEnvironment,
|
|
||||||
signature: ArWallet | SignatureType,
|
|
||||||
useBundler = false
|
|
||||||
): 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 } = sourceData;
|
||||||
|
|
||||||
this.signature = new Signature(this.warp, signature);
|
this.signature = new Signature(this.warp, wallet);
|
||||||
const signer = this.signature.signer;
|
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;
|
||||||
@@ -122,7 +118,7 @@ export class SourceImpl implements Source {
|
|||||||
srcTx.addTag(SmartWeaveTags.WASM_META, JSON.stringify(metadata));
|
srcTx.addTag(SmartWeaveTags.WASM_META, JSON.stringify(metadata));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (env === 'testnet') {
|
if (this.warp.environment === 'testnet') {
|
||||||
srcTx.addTag(SmartWeaveTags.WARP_TESTNET, '1.0.0');
|
srcTx.addTag(SmartWeaveTags.WARP_TESTNET, '1.0.0');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,17 +126,40 @@ export class SourceImpl implements Source {
|
|||||||
|
|
||||||
this.logger.debug('Posting transaction with source');
|
this.logger.debug('Posting transaction with source');
|
||||||
|
|
||||||
// note: in case of useBundler = true, we're posting both
|
return srcTx;
|
||||||
// src tx and contract tx in one request.
|
}
|
||||||
let responseOk = true;
|
|
||||||
|
async saveSourceTx(srcTx: Transaction, disableBundling: boolean = false): Promise<string> {
|
||||||
|
this.logger.debug('Saving contract source', srcTx.id);
|
||||||
|
|
||||||
|
if (this.warp.environment == 'local') {
|
||||||
|
disableBundling = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const effectiveUseBundler =
|
||||||
|
disableBundling == undefined ? this.warp.definitionLoader.type() == 'warp' : !disableBundling;
|
||||||
|
|
||||||
|
const tagsParser = new TagsParser();
|
||||||
|
const signatureTag = tagsParser.getTag(srcTx, SmartWeaveTags.SIGNATURE_TYPE);
|
||||||
|
|
||||||
|
if (signatureTag && signatureTag != 'arweave' && !effectiveUseBundler) {
|
||||||
|
throw new Error(`Unable to save source with signature type: ${signatureTag} when bundling is disabled.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
let responseOk: boolean;
|
||||||
let response: { status: number; statusText: string; data: any };
|
let response: { status: number; statusText: string; data: any };
|
||||||
if (!useBundler) {
|
|
||||||
|
if (!disableBundling) {
|
||||||
|
const result = await this.postSource(srcTx);
|
||||||
|
this.logger.debug(result);
|
||||||
|
responseOk = true;
|
||||||
|
} else {
|
||||||
response = await this.warp.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;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (responseOk) {
|
if (responseOk) {
|
||||||
return srcTx;
|
return srcTx.id;
|
||||||
} else {
|
} else {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Unable to write Contract Source. Arweave responded with status ${response.status}: ${response.statusText}`
|
`Unable to write Contract Source. Arweave responded with status ${response.status}: ${response.statusText}`
|
||||||
@@ -187,6 +206,26 @@ export class SourceImpl implements Source {
|
|||||||
|
|
||||||
return outputStreamBuffer.getContents();
|
return outputStreamBuffer.getContents();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async postSource(srcTx: Transaction = null): Promise<any> {
|
||||||
|
const response = await fetch(`${WARP_GW_URL}/gateway/sources/deploy`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({ srcTx }),
|
||||||
|
headers: {
|
||||||
|
'Accept-Encoding': 'gzip, deflate, br',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Accept: 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
return response.json();
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
`Error while posting contract source. Sequencer responded with status ${response.status} ${response.statusText}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function dummyImports(moduleImports: WebAssembly.ModuleImportDescriptor[]) {
|
function dummyImports(moduleImports: WebAssembly.ModuleImportDescriptor[]) {
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
import Arweave from 'arweave';
|
import Arweave from 'arweave';
|
||||||
import { Contract, InnerCallData } from '../contract/Contract';
|
import { Contract, InnerCallData } from '../contract/Contract';
|
||||||
import { CreateContract } from '../contract/deploy/CreateContract';
|
import {
|
||||||
|
ArWallet,
|
||||||
|
ContractData,
|
||||||
|
ContractDeploy,
|
||||||
|
CreateContract,
|
||||||
|
FromSrcTxContractData
|
||||||
|
} from '../contract/deploy/CreateContract';
|
||||||
import { DefaultCreateContract } from '../contract/deploy/impl/DefaultCreateContract';
|
import { DefaultCreateContract } from '../contract/deploy/impl/DefaultCreateContract';
|
||||||
import { HandlerBasedContract } from '../contract/HandlerBasedContract';
|
import { HandlerBasedContract } from '../contract/HandlerBasedContract';
|
||||||
import { PstContract } from '../contract/PstContract';
|
import { PstContract } from '../contract/PstContract';
|
||||||
@@ -15,6 +21,9 @@ import { WarpBuilder } from './WarpBuilder';
|
|||||||
import { WarpPluginType, WarpPlugin, knownWarpPlugins } from './WarpPlugin';
|
import { WarpPluginType, WarpPlugin, knownWarpPlugins } from './WarpPlugin';
|
||||||
import { SortKeyCache } from '../cache/SortKeyCache';
|
import { SortKeyCache } from '../cache/SortKeyCache';
|
||||||
import { ContractDefinition } from './ContractDefinition';
|
import { ContractDefinition } from './ContractDefinition';
|
||||||
|
import { SignatureType } from '../contract/Signature';
|
||||||
|
import { SourceData } from '../contract/deploy/impl/SourceImpl';
|
||||||
|
import Transaction from 'arweave/node/lib/transaction';
|
||||||
|
|
||||||
export type WarpEnvironment = 'local' | 'testnet' | 'mainnet' | 'custom';
|
export type WarpEnvironment = 'local' | 'testnet' | 'mainnet' | 'custom';
|
||||||
|
|
||||||
@@ -27,6 +36,9 @@ export type WarpEnvironment = 'local' | 'testnet' | 'mainnet' | 'custom';
|
|||||||
* contract and perform operations on them (see {@link Contract})
|
* contract and perform operations on them (see {@link Contract})
|
||||||
*/
|
*/
|
||||||
export class Warp {
|
export class Warp {
|
||||||
|
/**
|
||||||
|
* @deprecated createContract will be a private field, please use its methods directly e.g. await warp.deploy(...)
|
||||||
|
*/
|
||||||
readonly createContract: CreateContract;
|
readonly createContract: CreateContract;
|
||||||
readonly testing: Testing;
|
readonly testing: Testing;
|
||||||
|
|
||||||
@@ -61,6 +73,26 @@ export class Warp {
|
|||||||
return new HandlerBasedContract<State>(contractTxId, this, callingContract, innerCallData);
|
return new HandlerBasedContract<State>(contractTxId, this, callingContract, innerCallData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async deploy(contractData: ContractData, disableBundling?: boolean): Promise<ContractDeploy> {
|
||||||
|
return await this.createContract.deploy(contractData, disableBundling);
|
||||||
|
}
|
||||||
|
|
||||||
|
async deployFromSourceTx(contractData: FromSrcTxContractData, disableBundling?: boolean): Promise<ContractDeploy> {
|
||||||
|
return await this.createContract.deployFromSourceTx(contractData, disableBundling);
|
||||||
|
}
|
||||||
|
|
||||||
|
async deployBundled(rawDataItem: Buffer): Promise<ContractDeploy> {
|
||||||
|
return await this.createContract.deployBundled(rawDataItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
async createSourceTx(sourceData: SourceData, wallet: ArWallet | SignatureType): Promise<Transaction> {
|
||||||
|
return await this.createContract.createSourceTx(sourceData, wallet);
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveSourceTx(srcTx: Transaction, disableBundling?: boolean): Promise<string> {
|
||||||
|
return await this.createContract.saveSourceTx(srcTx, disableBundling);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows to connect to a contract that conforms to the Profit Sharing Token standard
|
* Allows to connect to a contract that conforms to the Profit Sharing Token standard
|
||||||
* @param contractTxId
|
* @param contractTxId
|
||||||
|
|||||||
66
tools/data/js/token-evolve.js
Normal file
66
tools/data/js/token-evolve.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
export function handle(state, action) {
|
||||||
|
const balances = state.balances;
|
||||||
|
const canEvolve = state.canEvolve;
|
||||||
|
const input = action.input;
|
||||||
|
const caller = action.caller;
|
||||||
|
|
||||||
|
if (input.function === 'transfer') {
|
||||||
|
const target = input.target;
|
||||||
|
const qty = input.qty;
|
||||||
|
|
||||||
|
if (!Number.isInteger(qty)) {
|
||||||
|
throw new ContractError('Invalid value for "qty". Must be an integer');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!target) {
|
||||||
|
throw new ContractError('No target specified');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qty <= 0 || caller === target) {
|
||||||
|
throw new ContractError('Invalid token transfer');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (balances[caller] < qty) {
|
||||||
|
throw new ContractError(`Caller balance not high enough to send ${qty} token(s)!`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lower the token balance of the caller
|
||||||
|
balances[caller] -= qty;
|
||||||
|
if (target in balances) {
|
||||||
|
// Wallet already exists in state, add new tokens
|
||||||
|
balances[target] += qty;
|
||||||
|
} else {
|
||||||
|
// Wallet is new, set starting balance
|
||||||
|
balances[target] = qty;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { state };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.function === 'balance') {
|
||||||
|
const target = input.target;
|
||||||
|
const ticker = state.ticker;
|
||||||
|
|
||||||
|
if (typeof target !== 'string') {
|
||||||
|
throw new ContractError('Must specify target to get balance for');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof balances[target] !== 'number') {
|
||||||
|
throw new ContractError('Cannot get balance, target does not exist');
|
||||||
|
}
|
||||||
|
|
||||||
|
return { result: { target, ticker, balance: balances[target] + 555 } };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.function === 'evolve' && canEvolve) {
|
||||||
|
if (state.owner !== caller) {
|
||||||
|
throw new ContractError('Only the owner can evolve a contract.');
|
||||||
|
}
|
||||||
|
|
||||||
|
state.evolve = input.value;
|
||||||
|
|
||||||
|
return { state };
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ContractError(`No function supplied or function not recognised: "${input.function}"`);
|
||||||
|
}
|
||||||
69
tools/evolve.ts
Normal file
69
tools/evolve.ts
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
import { defaultCacheOptions, LoggerFactory, WarpFactory } from '../src';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import { JWKInterface } from 'arweave/node/lib/wallet';
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
let wallet: JWKInterface = readJSON('./.secrets/jwk.json');
|
||||||
|
LoggerFactory.INST.logLevel('info');
|
||||||
|
const logger = LoggerFactory.INST.create('evolve');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const warp = WarpFactory.forMainnet({ ...defaultCacheOptions, inMemory: true });
|
||||||
|
|
||||||
|
const jsContractSrc = fs.readFileSync(path.join(__dirname, 'data/js/token-pst.js'), 'utf8');
|
||||||
|
const newJsContractSrc = fs.readFileSync(path.join(__dirname, 'data/js/token-evolve.js'), 'utf8');
|
||||||
|
const owner = await warp.arweave.wallets.jwkToAddress(wallet);
|
||||||
|
const initialState = {
|
||||||
|
ticker: 'EXAMPLE_PST_TOKEN',
|
||||||
|
owner,
|
||||||
|
canEvolve: true,
|
||||||
|
balances: {
|
||||||
|
'uhE-QeYS8i4pmUtnxQyHD7dzXFNaJ9oMK-IM-QPNY6M': 10000000,
|
||||||
|
'33F0QHcb22W7LwWR1iRC8Az1ntZG09XQ03YWuw2ABqA': 23111222
|
||||||
|
},
|
||||||
|
wallets: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
const { contractTxId } = await warp.deploy({
|
||||||
|
wallet,
|
||||||
|
initState: JSON.stringify(initialState),
|
||||||
|
src: jsContractSrc
|
||||||
|
});
|
||||||
|
|
||||||
|
const contract = warp.contract<any>(contractTxId).connect(wallet);
|
||||||
|
|
||||||
|
const { result } = await contract.viewState<any>({
|
||||||
|
function: 'balance',
|
||||||
|
target: 'uhE-QeYS8i4pmUtnxQyHD7dzXFNaJ9oMK-IM-QPNY6M'
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Original result', result);
|
||||||
|
|
||||||
|
const srcTx = await warp.createSourceTx({ src: newJsContractSrc }, wallet);
|
||||||
|
const newSrcTxId = await warp.saveSourceTx(srcTx);
|
||||||
|
console.log('Save result', newSrcTxId);
|
||||||
|
await contract.evolve(newSrcTxId);
|
||||||
|
|
||||||
|
const { result: evolvedResult } = await contract.viewState<any>({
|
||||||
|
function: 'balance',
|
||||||
|
target: 'uhE-QeYS8i4pmUtnxQyHD7dzXFNaJ9oMK-IM-QPNY6M'
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Evolved result', evolvedResult);
|
||||||
|
} catch (e) {
|
||||||
|
logger.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function readJSON(path: string): JWKInterface {
|
||||||
|
const content = fs.readFileSync(path, 'utf-8');
|
||||||
|
try {
|
||||||
|
return JSON.parse(content);
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(`File "${path}" does not contain a valid JSON`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch((e) => console.error(e));
|
||||||
Reference in New Issue
Block a user