diff --git a/README.md b/README.md index 32797a6..d7bd443 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,10 @@ # warp-sdk -WarpSdk provides a Typescript API for interacting with warp protocol, the automation protocol for the Cosmos ecosystem. The SDK provides a simple way to interact with the warp protocol's contracts, allowing developers to perform operations such as creating and managing jobs, templates, and accounts. +The Warp SDK provides a Typescript API for interacting with Warp Protocol, the decentralized automation tool for the Cosmos ecosystem. Warp allows developers to create novel features or experiences for their users through cost-efficient, on-chain automation—no smart contract changes necessary. + +The SDK provides a simple way to interact with Warp Protocol's contracts to automatically execute transactions in the future based on any available on-chain data. The trigger for execution is referred to as a condition, and the corresponding job encompasses the executable message. Warp jobs are submitted to a job queue, where participants called keepers monitor the conditions and—once met—execute the pre-signed job message. + +Read on below, check out the [docs](https://docs.warp.money/) for more information, or [get in touch](https://docs.google.com/forms/d/e/1FAIpQLSeYGdWL9tIHC3BP2riYXtT_cyZVDMGKrrSH0JCPCdV3PtZGyg/viewform) with the team to start building with Warp. ## Installation @@ -30,7 +34,7 @@ const lcd = new LCDClient({ 'pisco-1': piscoLcdClientConfig, }); -const wallet = new Wallet(lcd, new MnemonicKey({ mnemonic: '' })); +const wallet = new Wallet(lcd, new MnemonicKey({ mnemonic: '...' })); const sdk = new WarpSdk(wallet, piscoLcdClientConfig); const sender = wallet.key.accAddress(piscoLcdClientConfig.prefix); @@ -46,20 +50,43 @@ const nextExecution = variable const condition = cond.uint(uint.env('time'), 'gt', uint.ref(nextExecution)); +const executions = [ + { + condition, + msgs: [msg.execute('terra10788fkzah89xrdm27zkj5yvhj9x3494lxawzm5qq3vvxcqz2yzaqyd3enk', { harvest: {} })], + }, +]; + +const recurring = true; +const durationDays = '30'; +const vars = [nextExecution]; + +const estimateJobRewardMsg = job + .estimate() + .recurring(recurring) + .durationDays(durationDays) + .vars(vars) + .executions(executions) + .compose(); + +const reward = await sdk.estimateJobReward(sender, estimateJobRewardMsg); + +const operationalAmount = await sdk.estimateJobFee(sender, estimateJobRewardMsg, reward.amount.toString()); + const createJobMsg = job .create() .name('eris-harvest') .description('This job harvests rewards for eris protoocl vaults each day.') .labels([]) - .recurring(true) - .requeueOnEvict(true) - .reward('50000') - .cond(condition) - .var(nextExecution) - .msg(msg.execute('terra10788fkzah89xrdm27zkj5yvhj9x3494lxawzm5qq3vvxcqz2yzaqyd3enk', { harvest: {} })) + .recurring(recurring) + .reward(reward.amount.toString()) + .operationalAmount(operationalAmount.amount.toString()) + .vars(vars) + .durationDays(durationDays) + .executions(executions) .compose(); -sdk.createJob(sender, createJobMsg).then((response) => { +sdk.createJob(sender, createJobMsg, [operationalAmount]).then((response) => { console.log(response); }); diff --git a/package.json b/package.json index 0bbea42..9a3f086 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "url": "https://github.com/terra-money/warp-sdk/issues" }, "homepage": "https://github.com/terra-money/warp-sdk#readme", - "version": "0.1.68", + "version": "0.2.1", "scripts": { "clean": "rm -rf dist", "build": "yarn clean && yarn build:node && yarn build:browser", @@ -42,9 +42,10 @@ "generate-types": { "contracts": [ "warp-controller", - "warp-account", "warp-resolver", - "warp-templates" + "warp-templates", + "warp-account", + "warp-account-tracker" ], "output": "src/types/contracts" }, diff --git a/src/composers/account.ts b/src/composers/account.ts index 5ffab94..7d1509e 100644 --- a/src/composers/account.ts +++ b/src/composers/account.ts @@ -1,11 +1,9 @@ import { warp_account, warp_resolver } from '../types'; -type GenericMsg = Extract; - export class AccountComposer { - generic(msgs: warp_resolver.CosmosMsgFor_Empty[]): GenericMsg { + msgs(msgs: warp_resolver.WarpMsg[]): Extract { return { - generic: { + warp_msgs: { msgs, }, }; diff --git a/src/composers/condition.ts b/src/composers/condition.ts index e80af2e..5955c49 100644 --- a/src/composers/condition.ts +++ b/src/composers/condition.ts @@ -19,11 +19,11 @@ export class ConditionComposer { } public string( - left: warp_resolver.ValueFor_String, + left: warp_resolver.StringValueFor_String, op: warp_resolver.StringOp, - right: warp_resolver.ValueFor_String + right: warp_resolver.StringValueFor_String ): warp_resolver.Condition { - const expr: warp_resolver.GenExprFor_ValueFor_StringAnd_StringOp = { left, op, right }; + const expr: warp_resolver.GenExprFor_StringValueFor_StringAnd_StringOp = { left, op, right }; return this.expr({ string: expr }); } @@ -161,37 +161,37 @@ export class DecimalValueComposer { } export class StringValueComposer { - public simple(value: string): warp_resolver.ValueFor_String { + public simple(value: string): warp_resolver.StringValueFor_String { return { simple: value }; } - public ref(ref: warp_resolver.Variable): warp_resolver.ValueFor_String { + public ref(ref: warp_resolver.Variable): warp_resolver.StringValueFor_String { return { ref: `$warp.variable.${variableName(ref)}` }; } } export class UpdateFnComposer { - public uint(value: warp_resolver.NumValueFor_Uint256And_NumExprOpAnd_IntFnOp): warp_resolver.UpdateFnValue { + public uint(value: warp_resolver.NumValueFor_Uint256And_NumExprOpAnd_IntFnOp): warp_resolver.FnValue { return { uint: value }; } - public int(value: warp_resolver.NumValueForInt128And_NumExprOpAnd_IntFnOp): warp_resolver.UpdateFnValue { + public int(value: warp_resolver.NumValueForInt128And_NumExprOpAnd_IntFnOp): warp_resolver.FnValue { return { int: value }; } - public decimal(value: warp_resolver.NumValueFor_Decimal256And_NumExprOpAnd_DecimalFnOp): warp_resolver.UpdateFnValue { + public decimal(value: warp_resolver.NumValueFor_Decimal256And_NumExprOpAnd_DecimalFnOp): warp_resolver.FnValue { return { decimal: value }; } - public timestamp(value: warp_resolver.NumValueForInt128And_NumExprOpAnd_IntFnOp): warp_resolver.UpdateFnValue { + public timestamp(value: warp_resolver.NumValueForInt128And_NumExprOpAnd_IntFnOp): warp_resolver.FnValue { return { timestamp: value }; } - public block_height(value: warp_resolver.NumValueForInt128And_NumExprOpAnd_IntFnOp): warp_resolver.UpdateFnValue { + public block_height(value: warp_resolver.NumValueForInt128And_NumExprOpAnd_IntFnOp): warp_resolver.FnValue { return { block_height: value }; } - public bool(value: warp_resolver.Variable): warp_resolver.UpdateFnValue { + public bool(value: warp_resolver.Variable): warp_resolver.FnValue { return { bool: `$warp.variable.${variableName(value)}` }; } } diff --git a/src/composers/index.ts b/src/composers/index.ts index 442cd00..4d8d7a0 100644 --- a/src/composers/index.ts +++ b/src/composers/index.ts @@ -41,7 +41,7 @@ export const composers = { string, cond, fn, - msg, + msg: {}, template, job, variable, diff --git a/src/composers/job.ts b/src/composers/job.ts index 8c7dbd0..620ecb8 100644 --- a/src/composers/job.ts +++ b/src/composers/job.ts @@ -1,3 +1,5 @@ +import { EstimateJobMsg } from 'sdk'; +import { Execution } from '../types'; import { warp_controller, warp_resolver } from '../types/contracts'; export class JobSequenceMsgComposer { @@ -39,14 +41,15 @@ export class JobSequenceMsgComposer { export class CreateJobMsgComposer { private _name: string | undefined; private _recurring: boolean | undefined; - private _requeue_on_evict: boolean | undefined; private _reward: warp_controller.Uint128 | undefined; private _description: string; private _labels: string[]; private _assetsToWithdraw: warp_controller.AssetInfo[] | undefined; - private _msgs: warp_resolver.CosmosMsgFor_Empty[] = []; private _vars: warp_resolver.Variable[] = []; - private _condition: warp_resolver.Condition | undefined; + private _executions: Execution[] = []; + private _durationDays: string; + private _fundingAccount: string; + private _operationalAmount: warp_controller.Uint128 | undefined; static new(): CreateJobMsgComposer { return new CreateJobMsgComposer(); @@ -62,11 +65,6 @@ export class CreateJobMsgComposer { return this; } - requeueOnEvict(requeue_on_evict: boolean): CreateJobMsgComposer { - this._requeue_on_evict = requeue_on_evict; - return this; - } - reward(reward: warp_controller.Uint128): CreateJobMsgComposer { this._reward = reward; return this; @@ -82,23 +80,33 @@ export class CreateJobMsgComposer { return this; } - assetsToWithdraw(assetsToWithdraw: warp_controller.AssetInfo[]): CreateJobMsgComposer { - this._assetsToWithdraw = assetsToWithdraw; + durationDays(durationDays: string): CreateJobMsgComposer { + this._durationDays = durationDays; + return this; + } + + fundingAccount(fundingAccount?: string): CreateJobMsgComposer { + this._fundingAccount = fundingAccount; return this; } - msg(msg: warp_resolver.CosmosMsgFor_Empty): CreateJobMsgComposer { - this._msgs.push(msg); + operationalAmount(operationalAmount: warp_controller.Uint128): CreateJobMsgComposer { + this._operationalAmount = operationalAmount; return this; } - cond(condition: warp_resolver.Condition): CreateJobMsgComposer { - this._condition = condition; + assetsToWithdraw(assetsToWithdraw: warp_controller.AssetInfo[]): CreateJobMsgComposer { + this._assetsToWithdraw = assetsToWithdraw; return this; } - var(variable: warp_resolver.Variable): CreateJobMsgComposer { - this._vars.push(variable); + executions(executions: Execution[]): CreateJobMsgComposer { + this._executions = executions; + return this; + } + + vars(vars: warp_resolver.Variable[]): CreateJobMsgComposer { + this._vars = vars; return this; } @@ -106,37 +114,90 @@ export class CreateJobMsgComposer { if ( this._name === undefined || this._recurring === undefined || - this._requeue_on_evict === undefined || this._reward === undefined || this._description === undefined || - this._labels === undefined + this._labels === undefined || + this._operationalAmount == undefined ) { throw new Error('All required fields must be provided'); } - if (this._condition === undefined) { - throw new Error('Condition must be provided'); - } - const createJobMsg: warp_controller.CreateJobMsg = { name: this._name, recurring: this._recurring, - requeue_on_evict: this._requeue_on_evict, reward: this._reward, description: this._description, labels: this._labels, - condition: JSON.stringify(this._condition), - msgs: JSON.stringify(this._msgs), + executions: this._executions.map((e) => ({ + condition: JSON.stringify(e.condition), + msgs: JSON.stringify(e.msgs), + })), + duration_days: this._durationDays, vars: JSON.stringify(this._vars), assets_to_withdraw: this._assetsToWithdraw, + operational_amount: this._operationalAmount, + funding_account: this._fundingAccount, }; return createJobMsg; } } +export class EstimateJobMsgComposer { + private _recurring: boolean | undefined; + private _vars: warp_resolver.Variable[] = []; + private _executions: Execution[] = []; + private _durationDays: string; + + static new(): EstimateJobMsgComposer { + return new EstimateJobMsgComposer(); + } + + recurring(recurring: boolean): EstimateJobMsgComposer { + this._recurring = recurring; + return this; + } + + durationDays(durationDays: string): EstimateJobMsgComposer { + this._durationDays = durationDays; + return this; + } + + executions(executions: Execution[]): EstimateJobMsgComposer { + this._executions = executions; + return this; + } + + vars(vars: warp_resolver.Variable[]): EstimateJobMsgComposer { + this._vars = vars; + return this; + } + + compose(): EstimateJobMsg { + if (this._recurring === undefined || this._durationDays === undefined) { + throw new Error('All required fields must be provided'); + } + + const estimateJobMsg: EstimateJobMsg = { + recurring: this._recurring, + executions: this._executions.map((e) => ({ + condition: JSON.stringify(e.condition), + msgs: JSON.stringify(e.msgs), + })), + duration_days: this._durationDays, + vars: JSON.stringify(this._vars), + }; + + return estimateJobMsg; + } +} + export class JobComposer { create(): CreateJobMsgComposer { return new CreateJobMsgComposer(); } + + estimate(): EstimateJobMsgComposer { + return new EstimateJobMsgComposer(); + } } diff --git a/src/composers/msg.ts b/src/composers/msg.ts index 5a0bd97..70b5079 100644 --- a/src/composers/msg.ts +++ b/src/composers/msg.ts @@ -3,36 +3,32 @@ import { warp_resolver } from '../types'; import { base64encode } from '../utils'; export class MessageComposer { - send(amount: warp_resolver.Coin[], to_address: string): warp_resolver.CosmosMsgFor_Empty { - return { bank: { send: { amount, to_address } } }; + send(amount: warp_resolver.Coin[], to_address: string): warp_resolver.WarpMsg { + return { generic: { bank: { send: { amount, to_address } } } }; } - burn(amount: warp_resolver.Coin[]): warp_resolver.CosmosMsgFor_Empty { - return { bank: { burn: { amount } } }; + burn(amount: warp_resolver.Coin[]): warp_resolver.WarpMsg { + return { generic: { bank: { burn: { amount } } } }; } - delegate(amount: warp_resolver.Coin, validator: string): warp_resolver.CosmosMsgFor_Empty { - return { staking: { delegate: { amount, validator } } }; + delegate(amount: warp_resolver.Coin, validator: string): warp_resolver.WarpMsg { + return { generic: { staking: { delegate: { amount, validator } } } }; } - undelegate(amount: warp_resolver.Coin, validator: string): warp_resolver.CosmosMsgFor_Empty { - return { staking: { undelegate: { amount, validator } } }; + undelegate(amount: warp_resolver.Coin, validator: string): warp_resolver.WarpMsg { + return { generic: { staking: { undelegate: { amount, validator } } } }; } - redelegate( - amount: warp_resolver.Coin, - dst_validator: string, - src_validator: string - ): warp_resolver.CosmosMsgFor_Empty { - return { staking: { redelegate: { amount, dst_validator, src_validator } } }; + redelegate(amount: warp_resolver.Coin, dst_validator: string, src_validator: string): warp_resolver.WarpMsg { + return { generic: { staking: { redelegate: { amount, dst_validator, src_validator } } } }; } - setWithdrawAddress(address: string): warp_resolver.CosmosMsgFor_Empty { - return { distribution: { set_withdraw_address: { address } } }; + setWithdrawAddress(address: string): warp_resolver.WarpMsg { + return { generic: { distribution: { set_withdraw_address: { address } } } }; } - withdrawDelegatorReward(validator: string): warp_resolver.CosmosMsgFor_Empty { - return { distribution: { withdraw_delegator_reward: { validator } } }; + withdrawDelegatorReward(validator: string): warp_resolver.WarpMsg { + return { generic: { distribution: { withdraw_delegator_reward: { validator } } } }; } transfer( @@ -40,23 +36,27 @@ export class MessageComposer { channel_id: string, timeout: warp_resolver.IbcTimeout, to_address: string - ): warp_resolver.CosmosMsgFor_Empty { - return { ibc: { transfer: { amount, channel_id, timeout, to_address } } }; + ): warp_resolver.WarpMsg { + return { generic: { ibc: { transfer: { amount, channel_id, timeout, to_address } } } }; } - sendPacket(channel_id: string, data: T, timeout: warp_resolver.IbcTimeout): warp_resolver.CosmosMsgFor_Empty { + sendPacket(channel_id: string, data: T, timeout: warp_resolver.IbcTimeout): warp_resolver.WarpMsg { return { - ibc: { send_packet: { channel_id, data: variableRefOrEncode(data), timeout } }, + generic: { + ibc: { send_packet: { channel_id, data: variableRefOrEncode(data), timeout } }, + }, }; } - closeChannel(channel_id: string): warp_resolver.CosmosMsgFor_Empty { - return { ibc: { close_channel: { channel_id } } }; + closeChannel(channel_id: string): warp_resolver.WarpMsg { + return { generic: { ibc: { close_channel: { channel_id } } } }; } - execute(contract_addr: string, msg: T, funds: warp_resolver.Coin[] = []): warp_resolver.CosmosMsgFor_Empty { + execute(contract_addr: string, msg: T, funds: warp_resolver.Coin[] = []): warp_resolver.WarpMsg { return { - wasm: { execute: { contract_addr, funds, msg: variableRefOrEncode(msg) } }, + generic: { + wasm: { execute: { contract_addr, funds, msg: variableRefOrEncode(msg) } }, + }, }; } @@ -66,24 +66,36 @@ export class MessageComposer { label: string, msg: T, funds: warp_resolver.Coin[] = [] - ): warp_resolver.CosmosMsgFor_Empty { - return { wasm: { instantiate: { admin, code_id, funds, label, msg: variableRefOrEncode(msg) } } }; + ): warp_resolver.WarpMsg { + return { generic: { wasm: { instantiate: { admin, code_id, funds, label, msg: variableRefOrEncode(msg) } } } }; + } + + migrate(contract_addr: string, msg: T, new_code_id: number): warp_resolver.WarpMsg { + return { generic: { wasm: { migrate: { contract_addr, msg: variableRefOrEncode(msg), new_code_id } } } }; } - migrate(contract_addr: string, msg: T, new_code_id: number): warp_resolver.CosmosMsgFor_Empty { - return { wasm: { migrate: { contract_addr, msg: variableRefOrEncode(msg), new_code_id } } }; + update_admin(admin: string, contract_addr: string): warp_resolver.WarpMsg { + return { generic: { wasm: { update_admin: { admin, contract_addr } } } }; } - update_admin(admin: string, contract_addr: string): warp_resolver.CosmosMsgFor_Empty { - return { wasm: { update_admin: { admin, contract_addr } } }; + clear_admin(contract_addr: string): warp_resolver.WarpMsg { + return { generic: { wasm: { clear_admin: { contract_addr } } } }; } - clear_admin(contract_addr: string): warp_resolver.CosmosMsgFor_Empty { - return { wasm: { clear_admin: { contract_addr } } }; + vote(proposal_id: number, vote: warp_resolver.VoteOption): warp_resolver.WarpMsg { + return { generic: { gov: { vote: { proposal_id, vote } } } }; } - vote(proposal_id: number, vote: warp_resolver.VoteOption): warp_resolver.CosmosMsgFor_Empty { - return { gov: { vote: { proposal_id, vote } } }; + withdrawAssets(msg: warp_resolver.WithdrawAssetsMsg): warp_resolver.WarpMsg { + return { + withdraw_assets: msg, + }; + } + + ibcTransfer(msg: warp_resolver.IbcTransferMsg): warp_resolver.WarpMsg { + return { + ibc_transfer: msg, + }; } } diff --git a/src/composers/template.ts b/src/composers/template.ts index 97352a1..b1ee3e8 100644 --- a/src/composers/template.ts +++ b/src/composers/template.ts @@ -1,7 +1,7 @@ -import { warp_resolver, warp_templates } from '../types'; +import { Execution, warp_resolver, warp_templates } from '../types'; export class SubmitTemplateMsgComposer { - private _condition: warp_resolver.Condition | undefined; + private _executions: Execution[] = []; private _formattedStr: string = ''; private _msgs: warp_resolver.CosmosMsgFor_Empty[] = []; private _name: string = ''; @@ -11,11 +11,6 @@ export class SubmitTemplateMsgComposer { return new SubmitTemplateMsgComposer(); } - cond(condition: warp_resolver.Condition): SubmitTemplateMsgComposer { - this._condition = condition; - return this; - } - formattedStr(formattedStr: string): SubmitTemplateMsgComposer { this._formattedStr = formattedStr; return this; @@ -31,8 +26,8 @@ export class SubmitTemplateMsgComposer { return this; } - msg(msg: warp_resolver.CosmosMsgFor_Empty): SubmitTemplateMsgComposer { - this._msgs.push(msg); + executions(executions: Execution[]): SubmitTemplateMsgComposer { + this._executions = executions; return this; } @@ -42,9 +37,11 @@ export class SubmitTemplateMsgComposer { } const submitTemplateMsg: warp_templates.SubmitTemplateMsg = { - condition: this._condition, formatted_str: this._formattedStr, - msg: JSON.stringify(this._msgs), + executions: this._executions.map((e) => ({ + condition: JSON.stringify(e.condition), + msgs: JSON.stringify(e.msgs), + })), name: this._name, vars: this._vars, }; diff --git a/src/composers/variable.ts b/src/composers/variable.ts index d667143..7ed9619 100644 --- a/src/composers/variable.ts +++ b/src/composers/variable.ts @@ -5,7 +5,7 @@ class StaticVariableComposer { private variable: warp_resolver.StaticVariable; constructor() { - this.variable = { kind: 'string', name: '', value: '', encode: false }; + this.variable = { kind: 'string', name: '', value: '', encode: false, init_fn: null, reinitialize: false }; } kind(kind: warp_resolver.VariableKind): StaticVariableComposer { @@ -28,7 +28,17 @@ class StaticVariableComposer { return this; } - onSuccess(fn: warp_resolver.UpdateFnValue): StaticVariableComposer { + reinitialize(value: boolean): StaticVariableComposer { + this.variable.reinitialize = value; + return this; + } + + onInit(value: warp_resolver.FnValue): StaticVariableComposer { + this.variable.init_fn = value; + return this; + } + + onSuccess(fn: warp_resolver.FnValue): StaticVariableComposer { this.variable.update_fn = { ...(this.variable.update_fn ?? {}), on_success: fn, @@ -37,7 +47,7 @@ class StaticVariableComposer { return this; } - onError(fn: warp_resolver.UpdateFnValue): StaticVariableComposer { + onError(fn: warp_resolver.FnValue): StaticVariableComposer { this.variable.update_fn = { ...(this.variable.update_fn ?? {}), on_error: fn, @@ -59,7 +69,7 @@ class ExternalVariableComposer { kind: 'string', name: '', reinitialize: false, - init_fn: {} as warp_resolver.ExternalExpr, + init_fn: null, encode: false, }; } @@ -94,7 +104,7 @@ class ExternalVariableComposer { return this; } - onSuccess(fn: warp_resolver.UpdateFnValue): ExternalVariableComposer { + onSuccess(fn: warp_resolver.FnValue): ExternalVariableComposer { this.variable.update_fn = { ...(this.variable.update_fn ?? {}), on_success: fn, @@ -103,7 +113,7 @@ class ExternalVariableComposer { return this; } - onError(fn: warp_resolver.UpdateFnValue): ExternalVariableComposer { + onError(fn: warp_resolver.FnValue): ExternalVariableComposer { this.variable.update_fn = { ...(this.variable.update_fn ?? {}), on_error: fn, @@ -125,7 +135,7 @@ class QueryVariableComposer { kind: 'string', name: '', reinitialize: false, - init_fn: {} as warp_resolver.QueryExpr, + init_fn: null, encode: false, }; } @@ -160,7 +170,7 @@ class QueryVariableComposer { return this; } - onSuccess(fn: warp_resolver.UpdateFnValue): QueryVariableComposer { + onSuccess(fn: warp_resolver.FnValue): QueryVariableComposer { this.variable.update_fn = { ...(this.variable.update_fn ?? {}), on_success: fn, @@ -169,7 +179,7 @@ class QueryVariableComposer { return this; } - onError(fn: warp_resolver.UpdateFnValue): QueryVariableComposer { + onError(fn: warp_resolver.FnValue): QueryVariableComposer { this.variable.update_fn = { ...(this.variable.update_fn ?? {}), on_error: fn, diff --git a/src/condition.ts b/src/condition.ts index 0731acf..a8d538d 100644 --- a/src/condition.ts +++ b/src/condition.ts @@ -6,49 +6,53 @@ import { Big } from 'big.js'; import { Wallet } from './wallet'; import { extractVariableName, resolveExternalVariable, variableName } from './variables'; import { ContractAddresses } from 'modules/chain'; +import { Job } from './types'; +import { WarpSdk } from 'sdk'; export class Condition { public wallet: Wallet; public contracts: ContractAddresses; + public sdk: WarpSdk; - constructor(wallet: Wallet, contracts: ContractAddresses) { + constructor(wallet: Wallet, contracts: ContractAddresses, sdk: WarpSdk) { this.wallet = wallet; this.contracts = contracts; + this.sdk = sdk; } - public resolveCond = async (cond: warp_resolver.Condition, vars: warp_resolver.Variable[]): Promise => { + public resolveCond = async (cond: warp_resolver.Condition, job: Job): Promise => { if ('and' in cond) { - const all = await Promise.all(cond.and.map((c) => this.resolveCond(c, vars))); + const all = await Promise.all(cond.and.map((c) => this.resolveCond(c, job))); return every(all); } if ('or' in cond) { - const all = await Promise.all(cond.or.map((c) => this.resolveCond(c, vars))); + const all = await Promise.all(cond.or.map((c) => this.resolveCond(c, job))); return some(all); } if ('not' in cond) { - return !this.resolveCond(cond.not, vars); + return !this.resolveCond(cond.not, job); } - return this.resolveExpr(cond.expr, vars); + return this.resolveExpr(cond.expr, job); }; - public resolveExpr = async (expr: warp_resolver.Expr, vars: warp_resolver.Variable[]): Promise => { + public resolveExpr = async (expr: warp_resolver.Expr, job: Job): Promise => { if ('string' in expr) { - return this.resolveExprString(expr.string, vars); + return this.resolveExprString(expr.string, job); } if ('int' in expr) { - return this.resolveExprNum(expr.int, vars); + return this.resolveExprNum(expr.int, job); } if ('uint' in expr) { - return this.resolveExprNum(expr.uint, vars); + return this.resolveExprNum(expr.uint, job); } if ('decimal' in expr) { - return this.resolveExprNum(expr.decimal, vars); + return this.resolveExprNum(expr.decimal, job); } if ('timestamp' in expr) { @@ -60,7 +64,7 @@ export class Condition { } if ('bool' in expr) { - return this.resolveExprBool(expr.bool, vars); + return this.resolveExprBool(expr.bool, job); } return false; @@ -83,25 +87,28 @@ export class Condition { }; public resolveExprString = async ( - expr: warp_resolver.GenExprFor_ValueFor_StringAnd_StringOp, - vars: warp_resolver.Variable[] + expr: warp_resolver.GenExprFor_StringValueFor_StringAnd_StringOp, + job: Job ): Promise => { - const left = await this.resolveStringValue(expr.left, vars); - const right = await this.resolveStringValue(expr.right, vars); + const left = await this.resolveStringValue(expr.left, job); + const right = await this.resolveStringValue(expr.right, job); return this.resolveStringOp(left, right, expr.op); }; - public resolveStringValue = async ( - value: warp_resolver.ValueFor_String, - vars: warp_resolver.Variable[] - ): Promise => { + public resolveStringValue = async (value: warp_resolver.StringValueFor_String, job: Job): Promise => { if ('simple' in value) { return String(value.simple); } + if ('env' in value) { + if (value.env === 'warp_account_addr') { + return job.account; + } + } + if ('ref' in value) { - return this.resolveVariable(this.variable(value.ref, vars), (v) => String(v)); + return this.resolveVariable(this.variable(value.ref, job), (v) => String(v)); } }; @@ -110,10 +117,10 @@ export class Condition { | warp_resolver.GenExprFor_NumValueForInt128And_NumExprOpAnd_IntFnOpAnd_NumOp | warp_resolver.GenExprFor_NumValueFor_Decimal256And_NumExprOpAnd_DecimalFnOpAnd_NumOp | warp_resolver.GenExprFor_NumValueFor_Uint256And_NumExprOpAnd_IntFnOpAnd_NumOp, - vars: warp_resolver.Variable[] + job: Job ): Promise => { - const left = await this.resolveNumValue(expr.left, vars); - const right = await this.resolveNumValue(expr.right, vars); + const left = await this.resolveNumValue(expr.left, job); + const right = await this.resolveNumValue(expr.right, job); return this.resolveNumOp(left, right, expr.op); }; @@ -123,22 +130,22 @@ export class Condition { | warp_resolver.NumValueForInt128And_NumExprOpAnd_IntFnOp | warp_resolver.NumValueFor_Decimal256And_NumExprOpAnd_DecimalFnOp | warp_resolver.NumValueFor_Uint256And_NumExprOpAnd_IntFnOp, - vars: warp_resolver.Variable[] + job: Job ): Promise => { if ('simple' in value) { return Big(value.simple); } if ('expr' in value) { - return this.resolveNumExpr(value.expr, vars); + return this.resolveNumExpr(value.expr, job); } if ('fn' in value) { - return this.resolveNumFn(value.fn, vars); + return this.resolveNumFn(value.fn, job); } if ('ref' in value) { - return this.resolveVariable(this.variable(value.ref, vars), (v) => Big(v)); + return this.resolveVariable(this.variable(value.ref, job), (v) => Big(v)); } if ('env' in value) { @@ -159,9 +166,9 @@ export class Condition { | warp_resolver.NumFnValueForInt128And_NumExprOpAnd_IntFnOp | warp_resolver.NumFnValueFor_Decimal256And_NumExprOpAnd_DecimalFnOp | warp_resolver.NumFnValueFor_Uint256And_NumExprOpAnd_IntFnOp, - vars: warp_resolver.Variable[] + job: Job ): Promise { - const val = await this.resolveNumValue(fn.right, vars); + const val = await this.resolveNumValue(fn.right, job); switch (fn.op) { case 'abs': return val.abs(); @@ -181,10 +188,10 @@ export class Condition { | warp_resolver.NumExprValueForInt128And_NumExprOpAnd_IntFnOp | warp_resolver.NumExprValueFor_Decimal256And_NumExprOpAnd_DecimalFnOp | warp_resolver.NumExprValueFor_Uint256And_NumExprOpAnd_IntFnOp, - vars: warp_resolver.Variable[] + job: Job ): Promise { - const left = await this.resolveNumValue(expr.left, vars); - const right = await this.resolveNumValue(expr.right, vars); + const left = await this.resolveNumValue(expr.left, job); + const right = await this.resolveNumValue(expr.right, job); switch (expr.op) { case 'add': @@ -200,9 +207,9 @@ export class Condition { } } - public variable(ref: string, vars: warp_resolver.Variable[]): warp_resolver.Variable { + public variable(ref: string, job: Job): warp_resolver.Variable { const name = extractVariableName(ref); - const v = vars.find((v) => variableName(v) === name); + const v = job.vars.find((v) => variableName(v) === name); if (!v) { throw Error(`Unknown variable reference: ${name}.`); @@ -211,8 +218,8 @@ export class Condition { return v; } - public resolveExprBool(ref: string, vars: warp_resolver.Variable[]): Promise { - const v = this.variable(ref, vars); + public resolveExprBool(ref: string, job: Job): Promise { + const v = this.variable(ref, job); return this.resolveVariable(v, (val) => (val === 'true' ? true : false)); } diff --git a/src/examples/example_astro.ts b/src/examples/example_astro.ts index 396d4a8..dd9f234 100644 --- a/src/examples/example_astro.ts +++ b/src/examples/example_astro.ts @@ -64,47 +64,69 @@ const astroReceived = variable const condition = cond.uint(uint.ref(astroReceived), 'gte', uint.simple(limitOrder.astroPurchaseAmount)); -const createJobMsg = job - .create() - .name('astroport-limit-order') - .requeueOnEvict(true) - .reward('50000') - .recurring(false) - .description('This job creates an astroport limit order.') - .labels([]) - .reward('50000') - .cond(condition) - .var(astroReceived) - .msg( - msg.execute( - astroportContract, - { - execute_swap_operations: { - max_spread: limitOrder.maxSpread, - minimum_receive: limitOrder.astroPurchaseAmount, - operations: [ - { - astro_swap: { - ask_asset_info: { - token: { - contract_addr: limitOrder.astroTokenContract, +const executions = [ + { + condition, + msgs: [ + msg.execute( + astroportContract, + { + execute_swap_operations: { + max_spread: limitOrder.maxSpread, + minimum_receive: limitOrder.astroPurchaseAmount, + operations: [ + { + astro_swap: { + ask_asset_info: { + token: { + contract_addr: limitOrder.astroTokenContract, + }, }, - }, - offer_asset_info: { - native_token: { - denom: 'uluna', + offer_asset_info: { + native_token: { + denom: 'uluna', + }, }, }, }, - }, - ], + ], + }, }, - }, - [{ denom: 'uluna', amount: limitOrder.lunaOfferAmount }] - ) - ) + [{ denom: 'uluna', amount: limitOrder.lunaOfferAmount }] + ), + ], + }, +]; + +const recurring = false; +const durationDays = '30'; +const vars = [astroReceived]; + +const estimateJobRewardMsg = job + .estimate() + .recurring(recurring) + .durationDays(durationDays) + .vars(vars) + .executions(executions) + .compose(); + +const reward = await sdk.estimateJobReward(sender, estimateJobRewardMsg); + +const operationalAmount = await sdk.estimateJobFee(sender, estimateJobRewardMsg, reward.amount.toString()); + +const createJobMsg = job + .create() + .name('astroport-limit-order') + .reward(reward.amount.toString()) + .operationalAmount(operationalAmount.amount.toString()) + .recurring(recurring) + .description('This job creates an astroport limit order.') + .labels([]) + .vars(vars) + .durationDays(durationDays) + .executions(executions) .compose(); -sdk.createJob(sender, createJobMsg).then((response) => { +sdk.createJob(sender, createJobMsg, [operationalAmount]).then((response) => { console.log(response); }); diff --git a/src/examples/example_cross_chain.ts b/src/examples/example_cross_chain.ts new file mode 100644 index 0000000..7159d13 --- /dev/null +++ b/src/examples/example_cross_chain.ts @@ -0,0 +1,207 @@ +export { TerraTxError } from '../wallet/utils'; +import dotenv from 'dotenv'; +import { LCDClient, LCDClientConfig, MnemonicKey, Wallet } from '@terra-money/feather.js'; +import { WarpSdk } from '../sdk'; +import { uint, cond, msg, variable, job, query, account } from '../composers'; +import { addYears } from 'date-fns'; + +dotenv.config(); + +const piscoLcdClientConfig: LCDClientConfig = { + lcd: 'https://pisco-lcd.terra.dev', + chainID: 'pisco-1', + gasAdjustment: 1.75, + gasPrices: { uluna: 0.15 }, + prefix: 'terra', +}; + +const lcd = new LCDClient({ + 'pisco-1': piscoLcdClientConfig, +}); + +const wallet = new Wallet(lcd, new MnemonicKey({ mnemonic: '...' })); + +const sdk = new WarpSdk(wallet, piscoLcdClientConfig); +const sender = wallet.key.accAddress(piscoLcdClientConfig.prefix); + +const neutronRouter = 'neutron12jm24l9lr9cupufqjuxpdjnnweana4h66tsx5cl800mke26td26sq7m05p'; +const neutronBurnAccount = 'neutron109ylcvevv96f48jp6slzyuq8tu85g7u4xvmz3lq4ngw7d7r4smsq5q6ap4'; //todo +const terraRecipient = 'terra10e3392r94esu5dcwcuj9tt3mw5sq48uazarhpl77hfvcplfed04qhsrfn7'; //todo +const transferChannel = 'channel-3'; +const usdt = 'neutron1h6pztc3fn7h22jm60xx90tk7hln7xg8x0nazef58gqv0n4uw9vqq9khy43'; +const astro = 'ibc/EFB00E728F98F0C4BBE8CA362123ACAB466EDA2826DC6837E49F4C1902F21BBA'; + +let swapMsg = { + execute_swap_operations: { + operations: [ + { + astro_swap: { + offer_asset_info: { + native_token: { + denom: 'untrn', + }, + }, + ask_asset_info: { + token: { + contract_addr: usdt, + }, + }, + }, + }, + { + astro_swap: { + offer_asset_info: { + token: { + contract_addr: usdt, + }, + }, + ask_asset_info: { + native_token: { + denom: astro, + }, + }, + }, + }, + ], + minimum_receive: '$warp.variable.simulate_astro_amount', + }, +}; + +const swapVariable = variable + .static() + .kind('string') + .name('swap_msg') + .value(JSON.stringify(swapMsg)) + .encode(true) + .compose(); + +const routedSwapMsg = account.msgs([ + msg.execute(neutronRouter, variable.ref(swapVariable), [ + { denom: 'untrn', amount: '$warp.variable.neutron_balance' }, + ]), +]); + +const transferMsg = account.msgs([ + msg.transfer({ denom: astro, amount: '1' }, transferChannel, { timestamp: '1692915449102000000' }, terraRecipient), +]); + +let simulateAstroQuery = { + simulate_swap_operations: { + offer_amount: '$warp.variable.neutron_balance', + operations: [ + { + astro_swap: { + offer_asset_info: { + native_token: { + denom: 'untrn', + }, + }, + ask_asset_info: { + token: { + contract_addr: 'neutron1h6pztc3fn7h22jm60xx90tk7hln7xg8x0nazef58gqv0n4uw9vqq9khy43', + }, + }, + }, + }, + { + astro_swap: { + offer_asset_info: { + token: { + contract_addr: 'neutron1h6pztc3fn7h22jm60xx90tk7hln7xg8x0nazef58gqv0n4uw9vqq9khy43', + }, + }, + ask_asset_info: { + native_token: { + denom: 'ibc/EFB00E728F98F0C4BBE8CA362123ACAB466EDA2826DC6837E49F4C1902F21BBA', + }, + }, + }, + }, + ], + }, +}; + +const simulateAstroAmount = variable + .query() + .kind('uint') + .name('simulate_astro_amount') + .onInit({ query: query.smart(neutronRouter, simulateAstroQuery), selector: '$.amount' }) + .encode(false) + .compose(); + +const timeoutTimestamp = variable + .static() + .kind('uint') + .name('timeout_timestamp') + .encode(false) + .value(addYears(new Date(), 1).getTime() * 1000000 + '') + .compose(); + +const untrnAmount = variable + .query() + .kind('uint') + .name('neutron_balance') + .onInit({ query: query.balance(neutronBurnAccount, 'untrn'), selector: '$.amount.amount' }) //todo: create and get burn account on neutron + .encode(false) + .compose(); + +const routedSwapVariable = variable + .static() + .kind('string') + .name('routed_swap_msg') + .value(JSON.stringify(routedSwapMsg)) + .encode(true) + .compose(); + +const transferVariable = variable + .static() + .kind('string') + .name('transfer_msg') + .value(JSON.stringify(transferMsg)) + .encode(true) + .compose(); + +const condition = cond.uint(uint.ref(untrnAmount), 'gt', uint.simple('10000')); + +const executions = [ + { + condition, + msgs: [ + msg.execute(neutronBurnAccount, variable.ref(routedSwapVariable)), + msg.execute(neutronBurnAccount, variable.ref(transferVariable)), + ], + }, +]; + +const recurring = true; +const durationDays = '30'; +const vars = [untrnAmount, timeoutTimestamp, simulateAstroAmount, transferVariable, swapVariable, routedSwapVariable]; + +const estimateJobRewardMsg = job + .estimate() + .recurring(recurring) + .durationDays(durationDays) + .vars(vars) + .executions(executions) + .compose(); + +const reward = await sdk.estimateJobReward(sender, estimateJobRewardMsg); + +const operationalAmount = await sdk.estimateJobFee(sender, estimateJobRewardMsg, reward.amount.toString()); + +const createJobMsg = job + .create() + .name('swap-and-send') + .description('') + .recurring(recurring) + .reward(reward.amount.toString()) + .operationalAmount(operationalAmount.amount.toString()) + .vars(vars) + .executions(executions) + .durationDays(durationDays) + .labels([]) + .compose(); + +sdk.createJob(sender, createJobMsg, [operationalAmount]).then((response) => { + console.log(response); +}); diff --git a/src/examples/example_eris.ts b/src/examples/example_eris.ts index 06be1d1..2cbcd9c 100644 --- a/src/examples/example_eris.ts +++ b/src/examples/example_eris.ts @@ -30,19 +30,42 @@ const nextExecution = variable const condition = cond.uint(uint.env('time'), 'gt', uint.ref(nextExecution)); +const executions = [ + { + condition, + msgs: [msg.execute('terra10788fkzah89xrdm27zkj5yvhj9x3494lxawzm5qq3vvxcqz2yzaqyd3enk', { harvest: {} })], + }, +]; + +const recurring = true; +const durationDays = '30'; +const vars = [nextExecution]; + +const estimateJobRewardMsg = job + .estimate() + .recurring(recurring) + .durationDays(durationDays) + .vars(vars) + .executions(executions) + .compose(); + +const reward = await sdk.estimateJobReward(sender, estimateJobRewardMsg); + +const operationalAmount = await sdk.estimateJobFee(sender, estimateJobRewardMsg, reward.amount.toString()); + const createJobMsg = job .create() .name('eris-harvest') .description('This job harvests rewards for eris protoocl vaults each day.') .labels([]) - .recurring(true) - .requeueOnEvict(true) - .reward('50000') - .cond(condition) - .var(nextExecution) - .msg(msg.execute('terra10788fkzah89xrdm27zkj5yvhj9x3494lxawzm5qq3vvxcqz2yzaqyd3enk', { harvest: {} })) + .recurring(recurring) + .reward(reward.amount.toString()) + .operationalAmount(operationalAmount.amount.toString()) + .vars(vars) + .durationDays(durationDays) + .executions(executions) .compose(); -sdk.createJob(sender, createJobMsg).then((response) => { +sdk.createJob(sender, createJobMsg, [operationalAmount]).then((response) => { console.log(response); }); diff --git a/src/modules/chain.ts b/src/modules/chain.ts index ada6289..c853ee9 100644 --- a/src/modules/chain.ts +++ b/src/modules/chain.ts @@ -13,7 +13,7 @@ interface ContractDefinition { address: string; } -type ContractNames = 'warp-controller' | 'warp-resolver' | 'warp-templates'; +type ContractNames = 'warp-controller' | 'warp-resolver' | 'warp-templates' | 'warp-account-tracker'; type NetworkConfig = { [contract in ContractNames]: ContractDefinition; @@ -182,6 +182,7 @@ export interface ContractAddresses { controller: string; resolver: string; templates: string; + accountTracker: string; } export class ChainModule { @@ -199,6 +200,7 @@ export class ChainModule { controller: contractsConfig['warp-controller'].address, resolver: contractsConfig['warp-resolver'].address, templates: contractsConfig['warp-templates'].address, + accountTracker: contractsConfig['warp-account-tracker'].address, }; } @@ -278,6 +280,8 @@ export class ChainModule { return contractDefs['warp-resolver'].address; case 'templates': return contractDefs['warp-templates'].address; + case 'accountTracker': + return contractDefs['warp-account-tracker'].address; } } } diff --git a/src/modules/tx.ts b/src/modules/tx.ts index fee5ae1..7241094 100644 --- a/src/modules/tx.ts +++ b/src/modules/tx.ts @@ -1,8 +1,7 @@ -import { warp_account, warp_controller, warp_resolver, warp_templates } from '../types/contracts'; -import { base64encode, nativeTokenDenom, Token, TransferMsg } from '../utils'; -import { CreateTxOptions } from '@terra-money/feather.js'; +import { warp_controller, warp_account, warp_resolver, warp_templates } from '../types/contracts'; +import { base64encode, nativeTokenDenom, Token, TransferMsg, TransferNftMsg } from '../utils'; +import { Coins, CreateTxOptions } from '@terra-money/feather.js'; import { TxBuilder } from '../tx'; -import Big from 'big.js'; import { JobSequenceMsgComposer } from '../composers'; import { resolveExternalInputs } from '../variables'; import { WarpSdk } from '../sdk'; @@ -14,52 +13,49 @@ export class TxModule { this.warpSdk = warpSdk; } - public async createJob(sender: string, msg: warp_controller.CreateJobMsg): Promise { - const account = await this.warpSdk.account(sender); - const config = await this.warpSdk.config(); - - const nativeDenom = await nativeTokenDenom(this.warpSdk.wallet.lcd, this.warpSdk.chain.config.chainID); + public async createJob( + sender: string, + msg: warp_controller.CreateJobMsg, + coins?: Coins.Input + ): Promise { + const transferCwToControllerTx = await this.transferCwToController(sender, msg.cw_funds ?? []); return TxBuilder.new(this.warpSdk.chain.config) - .send(account.owner, account.account, { - [nativeDenom]: Big(msg.reward).mul(Big(config.creation_fee_percentage).add(100).div(100)).toString(), - }) + .tx(transferCwToControllerTx) .execute>( sender, this.warpSdk.chain.contracts.controller, { create_job: msg, - } + }, + coins ) .build(); } - public async createJobSequence(sender: string, sequence: warp_controller.CreateJobMsg[]): Promise { - const account = await this.warpSdk.account(sender); - const config = await this.warpSdk.config(); - + public async createJobSequence( + sender: string, + sequence: warp_controller.CreateJobMsg[], + coins?: Coins.Input + ): Promise { + let txBuilder = TxBuilder.new(this.warpSdk.chain.config); let jobSequenceMsgComposer = JobSequenceMsgComposer.new(); - let totalReward = Big(0); - sequence.forEach((msg) => { - totalReward = totalReward.add(Big(msg.reward)); + sequence.forEach(async (msg) => { jobSequenceMsgComposer = jobSequenceMsgComposer.chain(msg); + txBuilder = txBuilder.tx(await this.transferCwToController(sender, msg.cw_funds ?? [])); }); const jobSequenceMsg = jobSequenceMsgComposer.compose(); - const nativeDenom = await nativeTokenDenom(this.warpSdk.wallet.lcd, this.warpSdk.chain.config.chainID); - - return TxBuilder.new(this.warpSdk.chain.config) - .send(account.owner, account.account, { - [nativeDenom]: Big(totalReward).mul(Big(config.creation_fee_percentage).add(100).div(100)).toString(), - }) + return txBuilder .execute>( sender, this.warpSdk.chain.contracts.controller, { create_job: jobSequenceMsg, - } + }, + coins ) .build(); } @@ -77,26 +73,14 @@ export class TxModule { } public async updateJob(sender: string, msg: warp_controller.UpdateJobMsg): Promise { - const account = await this.warpSdk.account(sender); - const config = await this.warpSdk.config(); - const nativeDenom = await nativeTokenDenom(this.warpSdk.wallet.lcd, this.warpSdk.chain.config.chainID); - let txBuilder = TxBuilder.new(this.warpSdk.chain.config); - if (msg.added_reward) { - txBuilder = txBuilder.send(account.owner, account.account, { - [nativeDenom]: Big(msg.added_reward).mul(Big(config.creation_fee_percentage).add(100).div(100)).toString(), - }); - } - return txBuilder .execute>( sender, this.warpSdk.chain.contracts.controller, { - update_job: { - ...msg, - }, + update_job: msg, } ) .build(); @@ -132,6 +116,19 @@ export class TxModule { .build(); } + public async createFundingAccount(sender: string, funds?: Coins.Input): Promise { + return TxBuilder.new(this.warpSdk.chain.config) + .execute>( + sender, + this.warpSdk.chain.contracts.controller, + { + create_funding_account: {}, + }, + funds + ) + .build(); + } + public async submitTemplate(sender: string, msg: warp_templates.SubmitTemplateMsg): Promise { const config = await this.warpSdk.config(); @@ -256,18 +253,50 @@ export class TxModule { .build(); } - public async createAccount(sender: string, funds?: warp_controller.Fund[]): Promise { - return TxBuilder.new(this.warpSdk.chain.config) - .execute>( - sender, - this.warpSdk.chain.contracts.controller, - { - create_account: { - funds, + public async transferCwToController(sender: string, funds: warp_controller.CwFund[]): Promise { + let txBuilder = TxBuilder.new(this.warpSdk.chain.config); + + for (let fund of funds) { + if ('cw20' in fund) { + const { amount, contract_addr } = fund.cw20; + + txBuilder = txBuilder.execute(sender, contract_addr, { + transfer: { + amount, + recipient: this.warpSdk.chain.contracts.controller, }, - } - ) + }); + } else if ('cw721' in fund) { + const { contract_addr, token_id } = fund.cw721; + + txBuilder = txBuilder.execute(sender, contract_addr, { + transfer_nft: { + token_id, + recipient: this.warpSdk.chain.contracts.controller, + }, + }); + } + } + + return txBuilder.build(); + } + + public async withdrawAssets( + sender: string, + job_id: string, + msg: warp_account.WithdrawAssetsMsg + ): Promise { + const job = await this.warpSdk.job(job_id); + + const txPayload = TxBuilder.new(this.warpSdk.chain.config) + .execute>(sender, job.account, { + warp_msgs: { + msgs: [{ withdraw_assets: msg }], + }, + }) .build(); + + return txPayload; } public async depositToAccount( @@ -296,25 +325,13 @@ export class TxModule { return txPayload; } - public async withdrawAssets(sender: string, msg: warp_account.WithdrawAssetsMsg): Promise { - const { account } = await this.warpSdk.account(sender); - - const txPayload = TxBuilder.new(this.warpSdk.chain.config) - .execute>(sender, account, { - withdraw_assets: msg, - }) - .build(); - - return txPayload; - } - public async withdrawFromAccount( sender: string, + account: string, receiver: string, token: Token, amount: string ): Promise { - const { account } = await this.warpSdk.account(sender); let txPayload: CreateTxOptions; if (token.type === 'cw20') { @@ -327,14 +344,16 @@ export class TxModule { txPayload = TxBuilder.new(this.warpSdk.chain.config) .execute(sender, account, { - generic: { + warp_msgs: { msgs: [ { - wasm: { - execute: { - contract_addr: token.token, - msg: base64encode(transferMsg), - funds: [], + generic: { + wasm: { + execute: { + contract_addr: token.token, + msg: base64encode(transferMsg), + funds: [], + }, }, }, }, @@ -345,13 +364,15 @@ export class TxModule { } else { txPayload = TxBuilder.new(this.warpSdk.chain.config) .execute(sender, account, { - generic: { + warp_msgs: { msgs: [ { - bank: { - send: { - amount: [{ amount, denom: token.denom }], - to_address: receiver, + generic: { + bank: { + send: { + amount: [{ amount, denom: token.denom }], + to_address: receiver, + }, }, }, }, diff --git a/src/refs.injective.json b/src/refs.injective.json index 6cb9092..96b0dd1 100644 --- a/src/refs.injective.json +++ b/src/refs.injective.json @@ -11,20 +11,28 @@ "warp-templates": { "codeId": "2530", "address": "inj1lqe55634npthzavxd9stfpz0snr5hh0qxhhsgp" + }, + "warp-account-tracker": { + "codeId": "11630", + "address": "inj1zzgg30ygltd5s3xtescfquwmm2jktaq28t37f2j9h5wwswpxtyyspugek8" } }, "mainnet": { "warp-controller": { - "codeId": "71", - "address": "inj1l5xlk2eerslrd8va5qu6wd8yzc8yz8hdc579fg" + "codeId": "504", + "address": "inj1rnpwwhfg4q82rt3ylr8acyv99hwc57rdrrqnt4" }, "warp-resolver": { - "codeId": "70", - "address": "inj147v6a22ue68uumjxgz3t93g06w8vrlv7mmfuyf" + "codeId": "505", + "address": "inj14dln7eeucds7skjkkxqgesth46rl09zl7hlrfu" }, "warp-templates": { - "codeId": "82", - "address": "inj1nxp6uvz0506u32hf438uqy3cqs023k9wq6kxp8" + "codeId": "506", + "address": "inj1872z8v0zldhckg2a2yx7yjf3nmtvfc2x5llxpd" + }, + "warp-account-tracker": { + "codeId": "503", + "address": "inj1ng0hfwuan93cjmkf4xyrp95r589ryk5pk3sze0" } } } diff --git a/src/refs.neutron.json b/src/refs.neutron.json index a80f14c..4978241 100644 --- a/src/refs.neutron.json +++ b/src/refs.neutron.json @@ -11,20 +11,28 @@ "warp-templates": { "codeId": "1475", "address": "neutron1a29vd6lltycyr2cfku0w4km3axeexcxut53t3wx397dw8jndfq4swlxw9d" + }, + "warp-account-tracker": { + "codeId": "11630", + "address": "neutron1zzgg30ygltd5s3xtescfquwmm2jktaq28t37f2j9h5wwswpxtyyspugek8" } }, "mainnet": { "warp-controller": { - "codeId": "200", - "address": "neutron12aavdpccvq7lyrr8zaq5f54hk5ux0lw0q5xstqzre6sm2cvyfw5stlmlr3" + "codeId": "741", + "address": "neutron1qx2u4fqlyy8gfx9r5vfz80pzh88j07zr45whx30tfpj45usnt6eszwv9nj" }, "warp-resolver": { - "codeId": "201", - "address": "neutron1dzax7rxeq7ju0t8rgr5afg8kfdaxpe296qdeymreq030ay4rcx2s6sgx9p" + "codeId": "742", + "address": "neutron1tqk6vgqyfvfk30w5khy0qpuvj0h2lnzkfeynv3hzuha3yys6s2zqwrxhje" }, "warp-templates": { - "codeId": "202", + "codeId": "743", "address": "neutron1hn43q3v92y4dgdgtc5p7g684zx9dn6ejr74gchntdnppsljd89usxqs2s9" + }, + "warp-account-tracker": { + "codeId": "740", + "address": "neutron1k889vqh8aarqs45d2c2kcm7krv6d030uzzkpfh5yd5kajqrt7x0sy52we9" } } } diff --git a/src/refs.nibiru.json b/src/refs.nibiru.json index c7ccd0a..f68c82f 100644 --- a/src/refs.nibiru.json +++ b/src/refs.nibiru.json @@ -11,6 +11,10 @@ "warp-templates": { "codeId": "15", "address": "nibi1cyd63pk2wuvjkqmhlvp9884z4h89rqtn8w8xgz9m28hjd2kzj2cq0q8fv4" + }, + "warp-account-tracker": { + "codeId": "11630", + "address": "nibi1zzgg30ygltd5s3xtescfquwmm2jktaq28t37f2j9h5wwswpxtyyspugek8" } }, "mainnet": { @@ -25,6 +29,10 @@ "warp-templates": { "codeId": "15", "address": "nibi1cyd63pk2wuvjkqmhlvp9884z4h89rqtn8w8xgz9m28hjd2kzj2cq0q8fv4" + }, + "warp-account-tracker": { + "codeId": "11630", + "address": "nibi1zzgg30ygltd5s3xtescfquwmm2jktaq28t37f2j9h5wwswpxtyyspugek8" } } } diff --git a/src/refs.terra.json b/src/refs.terra.json index e528fed..467a446 100644 --- a/src/refs.terra.json +++ b/src/refs.terra.json @@ -1,30 +1,41 @@ { "testnet": { + "warp-account": { + "codeId": "12858" + }, "warp-controller": { - "codeId": "12696", - "address": "terra1sa5xql5v097surc5nrc3gehgq95x56z0qn86fts6qq5r87c505fq3mzg8t" + "codeId": "12861", + "address": "terra1mmsl3mxq9n8a6dgye05pn0qlup7r24e2vyjkqgpe32pv3ehjgnes0jz5nc" }, "warp-resolver": { - "codeId": "12694", - "address": "terra1m0fphjm43v34pgjywsuaa2pxsf27dyk5qqd8x6tpa9da7mqcp2esm6jkkc" + "codeId": "12859", + "address": "terra1kjv3e7v7m03kk8lrjqr2j604vusxrpxadg6xjz89jucladh5m5gqqag8q7" }, "warp-templates": { - "codeId": "12695", - "address": "terra1h79anmpmg6gfa6zpg2apurje3yxrv4r80aj3lzwnhtxs0tld8zrsyhe7au" + "codeId": "12860", + "address": "terra155wp5wwvquqzg30r6luu4e9d95p7pexe3xjszhflcsqe5gpayd6smz5w6k" + }, + "warp-account-tracker": { + "codeId": "12862", + "address": "terra15yefd9r33wad527jrxphef8r0jr7n4chg4ehgq0lmrwsfsflaajq5ps2jz" } }, "mainnet": { "warp-controller": { - "codeId": "2495", - "address": "terra1mg93d4g69tsf3x6sa9nkmkzc9wl38gdrygu0sewwcwj6l2a4089sdd7fgj" + "codeId": "2624", + "address": "terra1w6j6w9kx29h6kssqstupppm8vytvh2phpul72wsz8ylluqdd44us5u95ny" }, "warp-resolver": { - "codeId": "1616", - "address": "terra13taf05fedehdfsguu0v24ssmrs9xfzn6wewwkxxlsdy2qmudhwfsvvgdm5" + "codeId": "2625", + "address": "terra1t7zxwt5a2aheyh5ykg9j6ax20kz2q6v9q5ty7t0vdshsadnjegjqu6yjd6" }, "warp-templates": { - "codeId": "1787", - "address": "terra1mcfu3tkd5h9zdwuserl3v6uzetv9xke8wyaaf9vx07p7shk6xlws3styfk" + "codeId": "2626", + "address": "terra1s94r56gkyyavx5xxea9kgzemh9yeh6q9h065sf3ngc2pfd7ua67srrq5gh" + }, + "warp-account-tracker": { + "codeId": "2623", + "address": "terra1mwrmemx45mmqylz3j2wsq0cscnajeynlxval8rqq6vxh30fqyassajfvdq" } } } diff --git a/src/sdk.ts b/src/sdk.ts index f0d98b8..2dcaa50 100644 --- a/src/sdk.ts +++ b/src/sdk.ts @@ -1,18 +1,31 @@ -import { warp_account, warp_controller, warp_resolver } from './types/contracts'; +import { warp_controller, warp_resolver, warp_account } from './types/contracts'; import { WalletLike, Wallet, wallet } from './wallet'; import { Condition } from './condition'; -import { base64encode, contractQuery, nativeTokenDenom, Token, TransferMsg } from './utils'; -import { CreateTxOptions, TxInfo, LCDClientConfig, LCDClient } from '@terra-money/feather.js'; -import { TxBuilder } from './tx'; +import { + contractQuery, + nativeTokenDenom, + Token, + computeBurnFee, + computeCreationFee, + computeMaintenanceFee, + calculateDurationDaysAdjustmentFactor, +} from './utils'; +import { TxInfo, LCDClientConfig, LCDClient, Coins, Coin } from '@terra-money/feather.js'; import Big from 'big.js'; -import { JobSequenceMsgComposer } from './composers'; -import { resolveExternalInputs } from './variables'; import { TxModule, ChainModule, ChainName, NetworkName } from './modules'; import { cosmosMsgToCreateTxMsg } from './utils'; import { warp_templates } from './types/contracts/warp_templates'; import { Job, parseJob } from './types/job'; +import { warp_account_tracker } from './types/contracts'; -const FEE_ADJUSTMENT_FACTOR = 3; +const FEE_ADJUSTMENT_FACTOR = 8; + +export type EstimateJobMsg = { + vars: string; + recurring: boolean; + executions: warp_controller.Execution[]; + duration_days: string; +}; export class WarpSdk { public wallet: Wallet; @@ -24,7 +37,7 @@ export class WarpSdk { this.wallet = wallet(walletLike, chainConfig); this.tx = new TxModule(this); this.chain = new ChainModule(chainConfig); - this.condition = new Condition(this.wallet, this.chain.contracts); + this.condition = new Condition(this.wallet, this.chain.contracts, this); } public static lcdClientConfig( @@ -45,7 +58,16 @@ export class WarpSdk { public async isJobActive(jobId: string): Promise { const job = await this.job(jobId); - return this.condition.resolveCond(job.condition, job.vars); + + for (let execution of job.executions) { + const isCondActive = this.condition.resolveCond(execution.condition, job); + + if (isCondActive) { + return true; + } + } + + return false; } public async jobs(opts: warp_controller.QueryJobsMsg = {}): Promise { @@ -133,31 +155,57 @@ export class WarpSdk { return response; } - public async hydrateMsgs(msg: warp_resolver.QueryHydrateMsgsMsg): Promise { + public async hydrateMsgs(msg: warp_resolver.QueryHydrateMsgsMsg): Promise { const response = await contractQuery< Extract, - warp_resolver.CosmosMsgFor_Empty[] + warp_resolver.WarpMsg[] >(this.wallet.lcd, this.chain.contracts.resolver, { query_hydrate_msgs: msg }); return response; } - public async account(owner: string): Promise { - const { account } = await contractQuery< - Extract, - warp_controller.AccountResponse - >(this.wallet.lcd, this.chain.contracts.controller, { query_account: { owner } }); + public async jobAccounts( + msg: warp_account_tracker.QueryJobAccountsMsg + ): Promise { + const response = await contractQuery< + Extract, + warp_account_tracker.JobAccountsResponse + >(this.wallet.lcd, this.chain.contracts.accountTracker, { query_job_accounts: msg }); - return account; + return response; } - public async accounts(opts: warp_controller.QueryAccountsMsg): Promise { - const { accounts } = await contractQuery< - Extract, - warp_controller.AccountsResponse - >(this.wallet.lcd, this.chain.contracts.controller, { query_accounts: opts }); + public async firstFreeJobAccount( + msg: warp_account_tracker.QueryFirstFreeJobAccountMsg + ): Promise { + const response = await contractQuery< + Extract, + warp_account_tracker.JobAccountResponse + >(this.wallet.lcd, this.chain.contracts.accountTracker, { query_first_free_job_account: msg }); - return accounts; + return response; + } + + public async firstFreeFundingAccount( + msg: warp_account_tracker.QueryFirstFreeJobAccountMsg + ): Promise { + const response = await contractQuery< + Extract, + warp_account_tracker.FundingAccountResponse + >(this.wallet.lcd, this.chain.contracts.accountTracker, { query_first_free_funding_account: msg }); + + return response; + } + + public async fundingAccounts( + msg: warp_account_tracker.QueryFundingAccountsMsg + ): Promise { + const response = await contractQuery< + Extract, + warp_account_tracker.FundingAccountsResponse + >(this.wallet.lcd, this.chain.contracts.accountTracker, { query_funding_accounts: msg }); + + return response; } public async config(): Promise { @@ -174,24 +222,66 @@ export class WarpSdk { return { ...controllerConfig, template_fee: templatesConfig.template_fee }; } - public async estimateJobReward( - sender: string, - createJobMsg: Omit - ): Promise { - const account = await this.account(sender); + public async state(): Promise { + const { state: controllerState } = await contractQuery< + Extract, + warp_controller.StateResponse + >(this.wallet.lcd, this.chain.contracts.controller, { query_state: {} }); + + return { ...controllerState }; + } - const hydratedVars = await this.hydrateVars({ vars: createJobMsg.vars }); + // if reward is not provided, reward estimate is used + public async estimateJobFee(sender: string, estimateJobMsg: EstimateJobMsg, reward?: string): Promise { + const state = await this.state(); + const config = await this.config(); + const denom = await this.nativeTokenDenom(); + const jobReward: Coin = reward ? new Coin(denom, reward) : await this.estimateJobReward(sender, estimateJobMsg); + + const jobRewardAmount = Big(jobReward.amount.toString()); + const burnFee = computeBurnFee(jobRewardAmount, config); + const maintenanceFee = computeMaintenanceFee(Big(estimateJobMsg.duration_days), config); + const creationFee = computeCreationFee(Big(state.q), config); + + const totalFee = jobRewardAmount.add(burnFee).add(creationFee).add(maintenanceFee); + + return new Coin(denom, totalFee.toFixed(0)); + } + + public async estimateJobReward(sender: string, estimateJobMsg: EstimateJobMsg): Promise { + const denom = await this.nativeTokenDenom(); + let estimatedReward = new Coin(denom, 0); + + for (let execution of estimateJobMsg.executions) { + estimatedReward = estimatedReward.add(await this.estimateJobExecutionReward(sender, estimateJobMsg, execution)); + } + + const config = await this.config(); + + if (Big(config.minimum_reward).gte(estimatedReward.amount.toString())) { + return new Coin(denom, config.minimum_reward); + } + + return new Coin(denom, estimatedReward.amount.toFixed(0)); + } + + public async estimateJobExecutionReward( + sender: string, + estimateJobMsg: EstimateJobMsg, + execution: warp_controller.Execution + ): Promise { + const hydratedVars = await this.hydrateVars({ vars: estimateJobMsg.vars }); const hydratedMsgs = await this.hydrateMsgs({ vars: hydratedVars, - msgs: createJobMsg.msgs, + msgs: execution.msgs, }); const msgs = []; msgs.push( ...( - await this.tx.executeHydrateVars(account.account, { + await this.tx.executeHydrateVars(sender, { vars: hydratedVars, }) ).msgs @@ -199,26 +289,26 @@ export class WarpSdk { msgs.push( ...( - await this.tx.executeHydrateMsgs(account.account, { + await this.tx.executeHydrateMsgs(sender, { vars: hydratedVars, - msgs: createJobMsg.msgs, + msgs: execution.msgs, }) ).msgs ); msgs.push( ...( - await this.tx.executeResolveCondition(account.account, { - condition: createJobMsg.condition, + await this.tx.executeResolveCondition(sender, { + condition: execution.condition, vars: hydratedVars, }) ).msgs ); - if (createJobMsg.recurring) { + if (estimateJobMsg.recurring) { msgs.push( ...( - await this.tx.executeApplyVarFn(account.account, { + await this.tx.executeApplyVarFn(sender, { vars: hydratedVars, status: 'Executed', }) @@ -226,9 +316,21 @@ export class WarpSdk { ); } - msgs.push(...hydratedMsgs.map((msg) => cosmosMsgToCreateTxMsg(account.account, msg))); + // check only cosmos msg for estimation + let transformedMsgs: warp_resolver.CosmosMsgFor_Empty[] = hydratedMsgs + .map((m) => { + if ('generic' in m) { + return m.generic; + } + + return null; + }) + .filter(Boolean); + + // works only for cosmos msgs + msgs.push(...transformedMsgs.map((msg) => cosmosMsgToCreateTxMsg(sender, msg))); - const accountInfo = await this.wallet.lcd.auth.accountInfo(account.account); + const accountInfo = await this.wallet.lcd.auth.accountInfo(sender); const fee = await this.wallet.lcd.tx.estimateFee( [ @@ -245,29 +347,23 @@ export class WarpSdk { const denom = await this.nativeTokenDenom(); - return Big(fee.amount.get(denom).amount.toString()).mul(FEE_ADJUSTMENT_FACTOR); + const durationDaysAdjustmentFactor = calculateDurationDaysAdjustmentFactor(Big(estimateJobMsg.duration_days)); + + return new Coin( + denom, + Big(fee.amount.get(denom).amount.toString()) + .mul(FEE_ADJUSTMENT_FACTOR) + .mul(durationDaysAdjustmentFactor) + .toString() + ); } public async nativeTokenDenom(): Promise { return nativeTokenDenom(this.wallet.lcd, this.chain.config.chainID); } - public async createJob(sender: string, msg: warp_controller.CreateJobMsg): Promise { - await this.createAccountIfNotExists(sender); - - const account = await this.account(sender); - const config = await this.config(); - - const nativeDenom = await nativeTokenDenom(this.wallet.lcd, this.chain.config.chainID); - - const txPayload = TxBuilder.new(this.chain.config) - .send(account.owner, account.account, { - [nativeDenom]: Big(msg.reward).mul(Big(config.creation_fee_percentage).add(100).div(100)).toString(), - }) - .execute>(sender, this.chain.contracts.controller, { - create_job: msg, - }) - .build(); + public async createJob(sender: string, msg: warp_controller.CreateJobMsg, coins?: Coins.Input): Promise { + const txPayload = await this.tx.createJob(sender, msg, coins); return this.wallet.tx(txPayload); } @@ -286,228 +382,84 @@ export class WarpSdk { * when cond3 active * then execute job3 */ - public async createJobSequence(sender: string, sequence: warp_controller.CreateJobMsg[]): Promise { - await this.createAccountIfNotExists(sender); - - const account = await this.account(sender); - const config = await this.config(); - - let jobSequenceMsgComposer = JobSequenceMsgComposer.new(); - let totalReward = Big(0); - - sequence.forEach((msg) => { - totalReward = totalReward.add(Big(msg.reward)); - jobSequenceMsgComposer = jobSequenceMsgComposer.chain(msg); - }); - - const jobSequenceMsg = jobSequenceMsgComposer.compose(); - - const nativeDenom = await nativeTokenDenom(this.wallet.lcd, this.chain.config.chainID); - - const txPayload = TxBuilder.new(this.chain.config) - .send(account.owner, account.account, { - [nativeDenom]: Big(totalReward).mul(Big(config.creation_fee_percentage).add(100).div(100)).toString(), - }) - .execute>(sender, this.chain.contracts.controller, { - create_job: jobSequenceMsg, - }) - .build(); + public async createJobSequence( + sender: string, + sequence: warp_controller.CreateJobMsg[], + coins?: Coins.Input + ): Promise { + const txPayload = await this.tx.createJobSequence(sender, sequence, coins); return this.wallet.tx(txPayload); } - public async createAccountIfNotExists(sender: string): Promise { - try { - const account = await this.account(sender); - return account; - } catch (err) { - // account not exists - await this.createAccount(sender); - return this.account(sender); - } - } - public async deleteJob(sender: string, jobId: string): Promise { - const txPayload = TxBuilder.new(this.chain.config) - .execute>(sender, this.chain.contracts.controller, { - delete_job: { id: jobId }, - }) - .build(); + const txPayload = await this.tx.deleteJob(sender, jobId); return this.wallet.tx(txPayload); } public async updateJob(sender: string, msg: warp_controller.UpdateJobMsg): Promise { - const txPayload = TxBuilder.new(this.chain.config) - .execute>(sender, this.chain.contracts.controller, { - update_job: msg, - }) - .build(); + const txPayload = await this.tx.updateJob(sender, msg); return this.wallet.tx(txPayload); } public async evictJob(sender: string, jobId: string): Promise { - const txPayload = TxBuilder.new(this.chain.config) - .execute>(sender, this.chain.contracts.controller, { - evict_job: { - id: jobId, - }, - }) - .build(); + const txPayload = await this.tx.evictJob(sender, jobId); return this.wallet.tx(txPayload); } public async executeJob(sender: string, jobId: string): Promise { - const job = await this.job(jobId); + const txPayload = await this.tx.executeJob(sender, jobId); - const externalInputs = await resolveExternalInputs(job.vars); + return this.wallet.tx(txPayload); + } - const txPayload = TxBuilder.new(this.chain.config) - .execute>(sender, this.chain.contracts.controller, { - execute_job: { id: job.id, external_inputs: externalInputs }, - }) - .build(); + public async createFundingAccount(sender: string, funds?: Coins.Input): Promise { + const txPayload = await this.tx.createFundingAccount(sender, funds); return this.wallet.tx(txPayload); } public async submitTemplate(sender: string, msg: warp_templates.SubmitTemplateMsg): Promise { - const config = await this.config(); - - const nativeDenom = await nativeTokenDenom(this.wallet.lcd, this.chain.config.chainID); - - const txPayload = TxBuilder.new(this.chain.config) - .execute>( - sender, - this.chain.contracts.templates, - { - submit_template: msg, - }, - { - [nativeDenom]: config.template_fee, - } - ) - .build(); + const txPayload = await this.tx.submitTemplate(sender, msg); return this.wallet.tx(txPayload); } public async deleteTemplate(sender: string, templateId: string): Promise { - const txPayload = TxBuilder.new(this.chain.config) - .execute>(sender, this.chain.contracts.templates, { - delete_template: { id: templateId }, - }) - .build(); + const txPayload = await this.tx.deleteTemplate(sender, templateId); return this.wallet.tx(txPayload); } public async editTemplate(sender: string, msg: warp_templates.EditTemplateMsg): Promise { - const txPayload = TxBuilder.new(this.chain.config) - .execute>(sender, this.chain.contracts.templates, { - edit_template: msg, - }) - .build(); + const txPayload = await this.tx.editTemplate(sender, msg); return this.wallet.tx(txPayload); } - public async createAccount(sender: string, funds?: warp_controller.Fund[]): Promise { - const txPayload = TxBuilder.new(this.chain.config) - .execute>(sender, this.chain.contracts.controller, { - create_account: { - funds, - }, - }) - .build(); + public async withdrawAssets(sender: string, job_id: string, msg: warp_account.WithdrawAssetsMsg): Promise { + const txPayload = await this.tx.withdrawAssets(sender, job_id, msg); return this.wallet.tx(txPayload); } - public async withdrawAssets(sender: string, msg: warp_account.WithdrawAssetsMsg): Promise { - const { account } = await this.account(sender); - - const tx = TxBuilder.new(this.chain.config) - .execute>(sender, account, { - withdraw_assets: msg, - }) - .build(); - - return this.wallet.tx(tx); - } - - // deposit token (supports native, ibc and cw20 token type) from sender to warp account - // warp account can be owned by anyone public async depositToAccount(sender: string, account: string, token: Token, amount: string): Promise { - let txPayload: CreateTxOptions; - if (token.type === 'cw20') { - txPayload = TxBuilder.new(this.chain.config) - .execute(sender, token.token, { - transfer: { - amount, - recipient: account, - }, - }) - .build(); - } else { - txPayload = TxBuilder.new(this.chain.config) - .send(sender, account, { [token.denom]: amount }) - .build(); - } + const txPayload = await this.tx.depositToAccount(sender, account, token, amount); return this.wallet.tx(txPayload); } - // withdraw token (supports native, ibc and cw20 token type) from sender's warp account to receiver - // receiver can be anyone - public async withdrawFromAccount(sender: string, receiver: string, token: Token, amount: string): Promise { - const { account } = await this.account(sender); - let txPayload: CreateTxOptions; - if (token.type === 'cw20') { - const transferMsg = { - transfer: { - amount, - recipient: receiver, - }, - }; - - txPayload = TxBuilder.new(this.chain.config) - .execute(sender, account, { - generic: { - msgs: [ - { - wasm: { - execute: { - contract_addr: token.token, - msg: base64encode(transferMsg), - funds: [], - }, - }, - }, - ], - }, - }) - .build(); - } else { - txPayload = TxBuilder.new(this.chain.config) - .execute(sender, account, { - generic: { - msgs: [ - { - bank: { - send: { - amount: [{ amount, denom: token.denom }], - to_address: receiver, - }, - }, - }, - ], - }, - }) - .build(); - } + public async withdrawFromAccount( + sender: string, + account: string, + receiver: string, + token: Token, + amount: string + ): Promise { + const txPayload = await this.tx.withdrawFromAccount(sender, account, receiver, token, amount); return this.wallet.tx(txPayload); } diff --git a/src/tx.ts b/src/tx.ts index dbc251b..659b927 100644 --- a/src/tx.ts +++ b/src/tx.ts @@ -2,6 +2,7 @@ import { Coins, ExecuteContractProposal, MsgExecuteContract, + Msg, MsgSend, MsgSubmitProposal, MsgVote, @@ -9,8 +10,6 @@ import { CreateTxOptions, } from '@terra-money/feather.js'; -type Msg = MsgExecuteContract | MsgSubmitProposal | MsgVote | MsgSend; - export enum VoteOption { /** VOTE_OPTION_UNSPECIFIED - VOTE_OPTION_UNSPECIFIED defines a no-op vote option. */ VOTE_OPTION_UNSPECIFIED = 0, @@ -37,6 +36,11 @@ export class TxBuilder { this.chainConfig = chainConfig; } + tx(createTx: CreateTxOptions) { + this.msgs = [...this.msgs, ...createTx.msgs]; + return this; + } + execute(sender: string, contract: string, msg: T, coins?: Coins.Input) { this.msgs = [...this.msgs, new MsgExecuteContract(sender, contract, msg, coins)]; return this; diff --git a/src/types/contracts/index.ts b/src/types/contracts/index.ts index ec91606..f26b7a7 100644 --- a/src/types/contracts/index.ts +++ b/src/types/contracts/index.ts @@ -1,4 +1,5 @@ export * from './warp_account'; +export * from './warp_account_tracker'; export * from './warp_controller'; export * from './warp_resolver'; export * from './warp_templates'; diff --git a/src/types/contracts/warp_account.ts b/src/types/contracts/warp_account.ts index c91b0dc..55b090e 100644 --- a/src/types/contracts/warp_account.ts +++ b/src/types/contracts/warp_account.ts @@ -1,28 +1,22 @@ export module warp_account { export type Addr = string; - export interface AccountResponse { - account: Account; - } - export interface Account { - account: Addr; - owner: Addr; - } - export interface AccountsResponse { - accounts: Account[]; - } export interface Config { + creator_addr: Addr; owner: Addr; - warp_addr: Addr; } - export type ExecuteMsg = + export type ExecuteMsg = { + warp_msgs: WarpMsgs; + }; + export type Uint64 = string; + export type WarpMsg = | { - generic: GenericMsg; + generic: CosmosMsgFor_Empty; } | { - withdraw_assets: WithdrawAssetsMsg; + ibc_transfer: IbcTransferMsg; } | { - ibc_transfer: IbcTransferMsg; + withdraw_assets: WithdrawAssetsMsg; }; export type CosmosMsgFor_Empty = | { @@ -140,7 +134,6 @@ export module warp_account { }; }; export type Timestamp = Uint64; - export type Uint64 = string; export type WasmMsg = | { execute: { @@ -217,8 +210,9 @@ export module warp_account { */ cw721: [Addr, string]; }; - export interface GenericMsg { - msgs: CosmosMsgFor_Empty[]; + export interface WarpMsgs { + job_id?: Uint64 | null; + msgs: WarpMsg[]; } export interface Coin { amount: Uint128; @@ -239,9 +233,6 @@ export module warp_account { */ revision: number; } - export interface WithdrawAssetsMsg { - asset_infos: AssetInfo[]; - } export interface IbcTransferMsg { timeout_block_delta?: number | null; timeout_timestamp_seconds_delta?: number | null; @@ -261,7 +252,10 @@ export module warp_account { revision_height?: number | null; revision_number?: number | null; } - export type Fund = + export interface WithdrawAssetsMsg { + asset_infos: AssetInfo[]; + } + export type CwFund = | { cw20: Cw20Fund; } @@ -269,7 +263,10 @@ export module warp_account { cw721: Cw721Fund; }; export interface InstantiateMsg { - funds?: Fund[] | null; + cw_funds: CwFund[]; + job_id: Uint64; + msgs: WarpMsg[]; + native_funds: Coin[]; owner: string; } export interface Cw20Fund { @@ -280,29 +277,8 @@ export module warp_account { contract_addr: string; token_id: string; } - export type JobStatus = 'Pending' | 'Executed' | 'Failed' | 'Cancelled' | 'Evicted'; - export interface JobResponse { - job: Job; - } - export interface Job { - assets_to_withdraw: AssetInfo[]; - condition: string; - description: string; - id: Uint64; - labels: string[]; - last_update_time: Uint64; - msgs: string; - name: string; - owner: Addr; - recurring: boolean; - requeue_on_evict: boolean; - reward: Uint128; - status: JobStatus; - terminate_condition?: string | null; - vars: string; - } - export interface JobsResponse { - jobs: Job[]; - total_count: number; - } + export type QueryMsg = { + query_config: QueryConfigMsg; + }; + export interface QueryConfigMsg {} } diff --git a/src/types/contracts/warp_account_tracker.ts b/src/types/contracts/warp_account_tracker.ts new file mode 100644 index 0000000..f52c44a --- /dev/null +++ b/src/types/contracts/warp_account_tracker.ts @@ -0,0 +1,145 @@ +export module warp_account_tracker { + export type Addr = string; + export type AccountType = 'funding' | 'job'; + export interface Account { + account_addr: Addr; + account_type: AccountType; + owner_addr: Addr; + } + export interface AccountsResponse { + accounts: Account[]; + } + export interface Config { + admin: Addr; + warp_addr: Addr; + } + export interface ConfigResponse { + config: Config; + } + export type ExecuteMsg = + | { + take_job_account: TakeJobAccountMsg; + } + | { + free_job_account: FreeJobAccountMsg; + } + | { + take_funding_account: TakeFundingAccountMsg; + } + | { + free_funding_account: FreeFundingAccountMsg; + } + | { + update_config: UpdateConfigMsg; + }; + export type Uint64 = string; + export interface TakeJobAccountMsg { + account_addr: string; + account_owner_addr: string; + job_id: Uint64; + } + export interface FreeJobAccountMsg { + account_addr: string; + account_owner_addr: string; + last_job_id: Uint64; + } + export interface TakeFundingAccountMsg { + account_addr: string; + account_owner_addr: string; + job_id: Uint64; + } + export interface FreeFundingAccountMsg { + account_addr: string; + account_owner_addr: string; + job_id: Uint64; + } + export interface UpdateConfigMsg { + admin?: string | null; + } + export type AccountStatus = 'free' | 'taken'; + export interface FundingAccountResponse { + funding_account?: FundingAccount | null; + } + export interface FundingAccount { + account_addr: Addr; + account_status: AccountStatus; + taken_by_job_ids: Uint64[]; + } + export interface FundingAccountsResponse { + funding_accounts: FundingAccount[]; + total_count: number; + } + export interface InstantiateMsg { + admin: string; + warp_addr: string; + } + export interface JobAccountResponse { + job_account?: JobAccount | null; + } + export interface JobAccount { + account_addr: Addr; + account_status: AccountStatus; + taken_by_job_id: Uint64; + } + export interface JobAccountsResponse { + job_accounts: JobAccount[]; + total_count: number; + } + export type QueryMsg = + | { + query_config: QueryConfigMsg; + } + | { + query_accounts: QueryAccountsMsg; + } + | { + query_job_accounts: QueryJobAccountsMsg; + } + | { + query_job_account: QueryJobAccountMsg; + } + | { + query_first_free_job_account: QueryFirstFreeJobAccountMsg; + } + | { + query_funding_accounts: QueryFundingAccountsMsg; + } + | { + query_funding_account: QueryFundingAccountMsg; + } + | { + query_first_free_funding_account: QueryFirstFreeFundingAccountMsg; + }; + export interface QueryConfigMsg {} + export interface QueryAccountsMsg { + account_owner_addr: string; + limit?: number | null; + start_after?: string | null; + } + export interface QueryJobAccountsMsg { + account_owner_addr: string; + account_status: AccountStatus; + limit?: number | null; + start_after?: string | null; + } + export interface QueryJobAccountMsg { + account_addr: string; + account_owner_addr: string; + } + export interface QueryFirstFreeJobAccountMsg { + account_owner_addr: string; + } + export interface QueryFundingAccountsMsg { + account_owner_addr: string; + account_status: AccountStatus; + limit?: number | null; + start_after?: string | null; + } + export interface QueryFundingAccountMsg { + account_addr: string; + account_owner_addr: string; + } + export interface QueryFirstFreeFundingAccountMsg { + account_owner_addr: string; + } +} diff --git a/src/types/contracts/warp_controller.ts b/src/types/contracts/warp_controller.ts index c6c09e4..16b1b84 100644 --- a/src/types/contracts/warp_controller.ts +++ b/src/types/contracts/warp_controller.ts @@ -1,30 +1,26 @@ export module warp_controller { export type Addr = string; - export interface AccountResponse { - account: Account; - } - export interface Account { - account: Addr; - owner: Addr; - } - export interface AccountsResponse { - accounts: Account[]; - } export type Uint128 = string; export type Uint64 = string; export interface Config { - a_max: Uint128; - a_min: Uint128; - cancellation_fee_percentage: Uint64; - creation_fee_percentage: Uint64; + account_tracker_address: Addr; + burn_fee_min: Uint128; + burn_fee_rate: Uint128; + cancellation_fee_rate: Uint64; + creation_fee_max: Uint128; + creation_fee_min: Uint128; + duration_days_limit: Uint64; + duration_days_max: Uint64; + duration_days_min: Uint64; fee_collector: Addr; fee_denom: string; + maintenance_fee_max: Uint128; + maintenance_fee_min: Uint128; minimum_reward: Uint128; owner: Addr; - q_max: Uint64; + queue_size_left: Uint64; + queue_size_right: Uint64; resolver_address: Addr; - t_max: Uint64; - t_min: Uint64; warp_account_code_id: Uint64; } export interface ConfigResponse { @@ -46,9 +42,6 @@ export module warp_controller { | { evict_job: EvictJobMsg; } - | { - create_account: CreateAccountMsg; - } | { update_config: UpdateConfigMsg; } @@ -60,7 +53,197 @@ export module warp_controller { } | { migrate_finished_jobs: MigrateJobsMsg; + } + | { + create_funding_account: CreateFundingAccountMsg; + }; + export type WarpMsg = + | { + generic: CosmosMsgFor_Empty; + } + | { + ibc_transfer: IbcTransferMsg; + } + | { + withdraw_assets: WithdrawAssetsMsg; + }; + export type CosmosMsgFor_Empty = + | { + bank: BankMsg; + } + | { + custom: Empty; + } + | { + staking: StakingMsg; + } + | { + distribution: DistributionMsg; + } + | { + stargate: { + type_url: string; + value: Binary; + }; + } + | { + ibc: IbcMsg; + } + | { + wasm: WasmMsg; + } + | { + gov: GovMsg; + }; + export type BankMsg = + | { + send: { + amount: Coin[]; + to_address: string; + }; + } + | { + burn: { + amount: Coin[]; + }; + }; + export type StakingMsg = + | { + delegate: { + amount: Coin; + validator: string; + }; + } + | { + undelegate: { + amount: Coin; + validator: string; + }; + } + | { + redelegate: { + amount: Coin; + dst_validator: string; + src_validator: string; + }; + }; + export type DistributionMsg = + | { + set_withdraw_address: { + /** + * The `withdraw_address` + */ + address: string; + }; + } + | { + withdraw_delegator_reward: { + /** + * The `validator_address` + */ + validator: string; + }; + }; + export type Binary = string; + export type IbcMsg = + | { + transfer: { + /** + * packet data only supports one coin https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/ibc/applications/transfer/v1/transfer.proto#L11-L20 + */ + amount: Coin; + /** + * exisiting channel to send the tokens over + */ + channel_id: string; + /** + * when packet times out, measured on remote chain + */ + timeout: IbcTimeout; + /** + * address on the remote chain to receive these tokens + */ + to_address: string; + }; + } + | { + send_packet: { + channel_id: string; + data: Binary; + /** + * when packet times out, measured on remote chain + */ + timeout: IbcTimeout; + }; + } + | { + close_channel: { + channel_id: string; + }; }; + export type Timestamp = Uint64; + export type WasmMsg = + | { + execute: { + contract_addr: string; + funds: Coin[]; + /** + * msg is the json-encoded ExecuteMsg struct (as raw Binary) + */ + msg: Binary; + }; + } + | { + instantiate: { + admin?: string | null; + code_id: number; + funds: Coin[]; + /** + * A human-readbale label for the contract + */ + label: string; + /** + * msg is the JSON-encoded InstantiateMsg struct (as raw Binary) + */ + msg: Binary; + }; + } + | { + migrate: { + contract_addr: string; + /** + * msg is the json-encoded MigrateMsg struct that will be passed to the new code + */ + msg: Binary; + /** + * the code_id of the new logic to place in the given contract + */ + new_code_id: number; + }; + } + | { + update_admin: { + admin: string; + contract_addr: string; + }; + } + | { + clear_admin: { + contract_addr: string; + }; + }; + export type GovMsg = { + vote: { + proposal_id: number; + /** + * The vote option. + * + * This should be called "option" for consistency with Cosmos SDK. Sorry for that. See . + */ + vote: VoteOption; + }; + }; + export type VoteOption = 'yes' | 'no' | 'abstain' | 'no_with_veto'; export type AssetInfo = | { native: string; @@ -75,7 +258,7 @@ export module warp_controller { */ cw721: [Addr, string]; }; - export type Fund = + export type CwFund = | { cw20: Cw20Fund; } @@ -83,23 +266,78 @@ export module warp_controller { cw721: Cw721Fund; }; export interface CreateJobMsg { + account_msgs?: WarpMsg[] | null; assets_to_withdraw?: AssetInfo[] | null; - condition: string; + cw_funds?: CwFund[] | null; description: string; + duration_days: Uint64; + executions: Execution[]; + funding_account?: Addr | null; labels: string[]; - msgs: string; name: string; + operational_amount: Uint128; recurring: boolean; - requeue_on_evict: boolean; reward: Uint128; terminate_condition?: string | null; vars: string; } + export interface Coin { + amount: Uint128; + denom: string; + } + export interface Empty {} + export interface IbcTimeout { + block?: IbcTimeoutBlock | null; + timestamp?: Timestamp | null; + } + export interface IbcTimeoutBlock { + /** + * block height after which the packet times out. the height within the given revision + */ + height: number; + /** + * the version that the client is currently on (eg. after reseting the chain this could increment 1 as height drops to 0) + */ + revision: number; + } + export interface IbcTransferMsg { + timeout_block_delta?: number | null; + timeout_timestamp_seconds_delta?: number | null; + transfer_msg: TransferMsg; + } + export interface TransferMsg { + memo: string; + receiver: string; + sender: string; + source_channel: string; + source_port: string; + timeout_block?: TimeoutBlock | null; + timeout_timestamp?: number | null; + token?: Coin | null; + } + export interface TimeoutBlock { + revision_height?: number | null; + revision_number?: number | null; + } + export interface WithdrawAssetsMsg { + asset_infos: AssetInfo[]; + } + export interface Cw20Fund { + amount: Uint128; + contract_addr: string; + } + export interface Cw721Fund { + contract_addr: string; + token_id: string; + } + export interface Execution { + condition: string; + msgs: string; + } export interface DeleteJobMsg { id: Uint64; } export interface UpdateJobMsg { - added_reward?: Uint128 | null; description?: string | null; id: Uint64; labels?: string[] | null; @@ -116,30 +354,25 @@ export module warp_controller { export interface EvictJobMsg { id: Uint64; } - export interface CreateAccountMsg { - funds?: Fund[] | null; - } - export interface Cw20Fund { - amount: Uint128; - contract_addr: string; - } - export interface Cw721Fund { - contract_addr: string; - token_id: string; - } export interface UpdateConfigMsg { - a_max?: Uint128 | null; - a_min?: Uint128 | null; - cancellation_fee_percentage?: Uint64 | null; - creation_fee_percentage?: Uint64 | null; + burn_fee_min?: Uint128 | null; + burn_fee_rate?: Uint128 | null; + cancellation_fee_rate?: Uint64 | null; + creation_fee_max?: Uint128 | null; + creation_fee_min?: Uint128 | null; + duration_days_limit?: Uint64 | null; + duration_days_max?: Uint64 | null; + duration_days_min?: Uint64 | null; fee_collector?: string | null; + maintenance_fee_max?: Uint128 | null; + maintenance_fee_min?: Uint128 | null; minimum_reward?: Uint128 | null; owner?: string | null; - q_max?: Uint64 | null; - t_max?: Uint64 | null; - t_min?: Uint64 | null; + queue_size_left?: Uint64 | null; + queue_size_right?: Uint64 | null; } export interface MigrateAccountsMsg { + account_owner_addr: string; limit: number; start_after?: string | null; warp_account_code_id: Uint64; @@ -148,19 +381,26 @@ export module warp_controller { limit: number; start_after?: Uint64 | null; } + export interface CreateFundingAccountMsg {} export interface InstantiateMsg { - a_max: Uint128; - a_min: Uint128; - cancellation_fee: Uint64; - creation_fee: Uint64; + account_tracker_code_id: Uint64; + burn_fee_min: Uint128; + burn_fee_rate: Uint128; + cancellation_fee_rate: Uint64; + creation_fee_max: Uint128; + creation_fee_min: Uint128; + duration_days_limit: Uint64; + duration_days_max: Uint64; + duration_days_min: Uint64; fee_collector?: string | null; fee_denom: string; + maintenance_fee_max: Uint128; + maintenance_fee_min: Uint128; minimum_reward: Uint128; owner?: string | null; - q_max: Uint64; + queue_size_left: Uint64; + queue_size_right: Uint64; resolver_address: string; - t_max: Uint64; - t_min: Uint64; warp_account_code_id: Uint64; } export type JobStatus = 'Pending' | 'Executed' | 'Failed' | 'Cancelled' | 'Evicted'; @@ -168,17 +408,20 @@ export module warp_controller { job: Job; } export interface Job { + account: Addr; assets_to_withdraw: AssetInfo[]; - condition: string; + created_at_time: Uint64; description: string; + duration_days: Uint64; + executions: Execution[]; + funding_account?: Addr | null; id: Uint64; labels: string[]; last_update_time: Uint64; - msgs: string; name: string; owner: Addr; + prev_id?: Uint64 | null; recurring: boolean; - requeue_on_evict: boolean; reward: Uint128; status: JobStatus; terminate_condition?: string | null; @@ -196,13 +439,10 @@ export module warp_controller { query_jobs: QueryJobsMsg; } | { - query_account: QueryAccountMsg; - } - | { - query_accounts: QueryAccountsMsg; + query_config: QueryConfigMsg; } | { - query_config: QueryConfigMsg; + query_state: QueryStateMsg; }; export interface QueryJobMsg { id: Uint64; @@ -221,12 +461,13 @@ export module warp_controller { _0: Uint128; _1: Uint64; } - export interface QueryAccountMsg { - owner: string; + export interface QueryConfigMsg {} + export interface QueryStateMsg {} + export interface State { + current_job_id: Uint64; + q: Uint64; } - export interface QueryAccountsMsg { - limit?: number | null; - start_after?: string | null; + export interface StateResponse { + state: State; } - export interface QueryConfigMsg {} } diff --git a/src/types/contracts/warp_resolver.ts b/src/types/contracts/warp_resolver.ts index a70d28a..0ca7ced 100644 --- a/src/types/contracts/warp_resolver.ts +++ b/src/types/contracts/warp_resolver.ts @@ -27,7 +27,7 @@ export module warp_resolver { }; export type Expr = | { - string: GenExprFor_ValueFor_StringAnd_StringOp; + string: GenExprFor_StringValueFor_StringAnd_StringOp; } | { uint: GenExprFor_NumValueFor_Uint256And_NumExprOpAnd_IntFnOpAnd_NumOp; @@ -47,13 +47,17 @@ export module warp_resolver { | { bool: string; }; - export type ValueFor_String = + export type StringValueFor_String = | { simple: string; } | { ref: string; + } + | { + env: StringEnvValue; }; + export type StringEnvValue = 'warp_account_addr'; export type StringOp = 'starts_with' | 'ends_with' | 'contains' | 'eq' | 'neq'; export type NumValueFor_Uint256And_NumExprOpAnd_IntFnOp = | { @@ -112,10 +116,10 @@ export module warp_resolver { export type DecimalFnOp = 'abs' | 'neg' | 'floor' | 'sqrt' | 'ceil'; export type Uint64 = string; export type TimeOp = 'lt' | 'gt'; - export interface GenExprFor_ValueFor_StringAnd_StringOp { - left: ValueFor_String; + export interface GenExprFor_StringValueFor_StringAnd_StringOp { + left: StringValueFor_String; op: StringOp; - right: ValueFor_String; + right: StringValueFor_String; } export interface GenExprFor_NumValueFor_Uint256And_NumExprOpAnd_IntFnOpAnd_NumOp { left: NumValueFor_Uint256And_NumExprOpAnd_IntFnOp; @@ -382,6 +386,9 @@ export module warp_resolver { } | { execute_hydrate_msgs: ExecuteHydrateMsgsMsg; + } + | { + warp_msgs_to_cosmos_msgs: WarpMsgsToCosmosMsgsMsg; }; export type QueryRequestFor_String = | { @@ -489,18 +496,47 @@ export module warp_resolver { }; }; export type JobStatus = 'Pending' | 'Executed' | 'Failed' | 'Cancelled' | 'Evicted'; + export type WarpMsg = + | { + generic: CosmosMsgFor_Empty; + } + | { + ibc_transfer: IbcTransferMsg; + } + | { + withdraw_assets: WithdrawAssetsMsg; + }; + export type AssetInfo = + | { + native: string; + } + | { + cw20: Addr; + } + | { + /** + * @minItems 2 + * @maxItems 2 + */ + cw721: [Addr, string]; + }; + export type Addr = string; export interface ExecuteSimulateQueryMsg { query: QueryRequestFor_String; } export interface ExecuteValidateJobCreationMsg { - condition: string; - msgs: string; + executions: Execution[]; terminate_condition?: string | null; vars: string; } + export interface Execution { + condition: string; + msgs: string; + } export interface ExecuteHydrateVarsMsg { external_inputs?: ExternalInput[] | null; vars: string; + warp_account_addr?: string | null; } export interface ExternalInput { input: string; @@ -509,15 +545,43 @@ export module warp_resolver { export interface ExecuteResolveConditionMsg { condition: string; vars: string; + warp_account_addr?: string | null; } export interface ExecuteApplyVarFnMsg { status: JobStatus; vars: string; + warp_account_addr?: string | null; } export interface ExecuteHydrateMsgsMsg { msgs: string; vars: string; } + export interface WarpMsgsToCosmosMsgsMsg { + msgs: WarpMsg[]; + owner: Addr; + } + export interface IbcTransferMsg { + timeout_block_delta?: number | null; + timeout_timestamp_seconds_delta?: number | null; + transfer_msg: TransferMsg; + } + export interface TransferMsg { + memo: string; + receiver: string; + sender: string; + source_channel: string; + source_port: string; + timeout_block?: TimeoutBlock | null; + timeout_timestamp?: number | null; + token?: Coin | null; + } + export interface TimeoutBlock { + revision_height?: number | null; + revision_number?: number | null; + } + export interface WithdrawAssetsMsg { + asset_infos: AssetInfo[]; + } export interface InstantiateMsg {} export type QueryMsg = | { @@ -542,22 +606,24 @@ export module warp_resolver { query: QueryRequestFor_String; } export interface QueryValidateJobCreationMsg { - condition: string; - msgs: string; + executions: Execution[]; terminate_condition?: string | null; vars: string; } export interface QueryHydrateVarsMsg { external_inputs?: ExternalInput[] | null; vars: string; + warp_account_addr?: string | null; } export interface QueryResolveConditionMsg { condition: string; vars: string; + warp_account_addr?: string | null; } export interface QueryApplyVarFnMsg { status: JobStatus; vars: string; + warp_account_addr?: string | null; } export interface QueryHydrateMsgsMsg { msgs: string; @@ -576,8 +642,7 @@ export module warp_resolver { | { query: QueryVariable; }; - export type VariableKind = 'string' | 'uint' | 'int' | 'decimal' | 'timestamp' | 'bool' | 'amount' | 'asset' | 'json'; - export type UpdateFnValue = + export type FnValue = | { uint: NumValueFor_Uint256And_NumExprOpAnd_IntFnOp; } @@ -595,18 +660,24 @@ export module warp_resolver { } | { bool: string; + } + | { + string: StringValueFor_String; }; + export type VariableKind = 'string' | 'uint' | 'int' | 'decimal' | 'timestamp' | 'bool' | 'amount' | 'asset' | 'json'; export type Method = 'get' | 'post' | 'put' | 'patch' | 'delete'; export interface StaticVariable { encode: boolean; + init_fn: FnValue; kind: VariableKind; name: string; + reinitialize: boolean; update_fn?: UpdateFn | null; - value: string; + value?: string | null; } export interface UpdateFn { - on_error?: UpdateFnValue | null; - on_success?: UpdateFnValue | null; + on_error?: FnValue | null; + on_success?: FnValue | null; } export interface ExternalVariable { encode: boolean; diff --git a/src/types/contracts/warp_templates.ts b/src/types/contracts/warp_templates.ts index fc84e88..9c848c7 100644 --- a/src/types/contracts/warp_templates.ts +++ b/src/types/contracts/warp_templates.ts @@ -23,49 +23,38 @@ export module warp_templates { | { update_config: UpdateConfigMsg; }; - export type Condition = - | { - and: Condition[]; - } + export type Variable = | { - or: Condition[]; + static: StaticVariable; } | { - not: Condition; + external: ExternalVariable; } | { - expr: Expr; + query: QueryVariable; }; - export type Expr = - | { - string: GenExprFor_ValueFor_StringAnd_StringOp; - } + export type FnValue = | { - uint: GenExprFor_NumValueFor_Uint256And_NumExprOpAnd_IntFnOpAnd_NumOp; + uint: NumValueFor_Uint256And_NumExprOpAnd_IntFnOp; } | { - int: GenExprFor_NumValueForInt128And_NumExprOpAnd_IntFnOpAnd_NumOp; + int: NumValueForInt128And_NumExprOpAnd_IntFnOp; } | { - decimal: GenExprFor_NumValueFor_Decimal256And_NumExprOpAnd_DecimalFnOpAnd_NumOp; + decimal: NumValueFor_Decimal256And_NumExprOpAnd_DecimalFnOp; } | { - timestamp: TimeExpr; + timestamp: NumValueForInt128And_NumExprOpAnd_IntFnOp; } | { - block_height: BlockExpr; + block_height: NumValueForInt128And_NumExprOpAnd_IntFnOp; } | { bool: string; - }; - export type ValueFor_String = - | { - simple: string; } | { - ref: string; + string: StringValueFor_String; }; - export type StringOp = 'starts_with' | 'ends_with' | 'contains' | 'eq' | 'neq'; export type NumValueFor_Uint256And_NumExprOpAnd_IntFnOp = | { simple: Uint256; @@ -86,7 +75,6 @@ export module warp_templates { export type NumExprOp = 'add' | 'sub' | 'div' | 'mul' | 'mod'; export type IntFnOp = 'abs' | 'neg'; export type NumEnvValue = 'time' | 'block_height'; - export type NumOp = 'eq' | 'neq' | 'lt' | 'gt' | 'gte' | 'lte'; export type NumValueForInt128And_NumExprOpAnd_IntFnOp = | { simple: number; @@ -121,38 +109,18 @@ export module warp_templates { }; export type Decimal256 = string; export type DecimalFnOp = 'abs' | 'neg' | 'floor' | 'sqrt' | 'ceil'; - export type Uint64 = string; - export type TimeOp = 'lt' | 'gt'; - export type Variable = + export type StringValueFor_String = | { - static: StaticVariable; + simple: string; } | { - external: ExternalVariable; + ref: string; } | { - query: QueryVariable; + env: StringEnvValue; }; + export type StringEnvValue = 'warp_account_addr'; export type VariableKind = 'string' | 'uint' | 'int' | 'decimal' | 'timestamp' | 'bool' | 'amount' | 'asset' | 'json'; - export type UpdateFnValue = - | { - uint: NumValueFor_Uint256And_NumExprOpAnd_IntFnOp; - } - | { - int: NumValueForInt128And_NumExprOpAnd_IntFnOp; - } - | { - decimal: NumValueFor_Decimal256And_NumExprOpAnd_DecimalFnOp; - } - | { - timestamp: NumValueForInt128And_NumExprOpAnd_IntFnOp; - } - | { - block_height: NumValueForInt128And_NumExprOpAnd_IntFnOp; - } - | { - bool: string; - }; export type Method = 'get' | 'post' | 'put' | 'patch' | 'delete'; export type QueryRequestFor_String = | { @@ -260,22 +228,25 @@ export module warp_templates { contract_addr: string; }; }; + export type Uint64 = string; export interface SubmitTemplateMsg { - condition?: Condition | null; + executions: Execution[]; formatted_str: string; - msg: string; name: string; vars: Variable[]; } - export interface GenExprFor_ValueFor_StringAnd_StringOp { - left: ValueFor_String; - op: StringOp; - right: ValueFor_String; + export interface Execution { + condition: string; + msgs: string; } - export interface GenExprFor_NumValueFor_Uint256And_NumExprOpAnd_IntFnOpAnd_NumOp { - left: NumValueFor_Uint256And_NumExprOpAnd_IntFnOp; - op: NumOp; - right: NumValueFor_Uint256And_NumExprOpAnd_IntFnOp; + export interface StaticVariable { + encode: boolean; + init_fn: FnValue; + kind: VariableKind; + name: string; + reinitialize: boolean; + update_fn?: UpdateFn | null; + value?: string | null; } export interface NumExprValueFor_Uint256And_NumExprOpAnd_IntFnOp { left: NumValueFor_Uint256And_NumExprOpAnd_IntFnOp; @@ -286,11 +257,6 @@ export module warp_templates { op: IntFnOp; right: NumValueFor_Uint256And_NumExprOpAnd_IntFnOp; } - export interface GenExprFor_NumValueForInt128And_NumExprOpAnd_IntFnOpAnd_NumOp { - left: NumValueForInt128And_NumExprOpAnd_IntFnOp; - op: NumOp; - right: NumValueForInt128And_NumExprOpAnd_IntFnOp; - } export interface NumExprValueForInt128And_NumExprOpAnd_IntFnOp { left: NumValueForInt128And_NumExprOpAnd_IntFnOp; op: NumExprOp; @@ -300,11 +266,6 @@ export module warp_templates { op: IntFnOp; right: NumValueForInt128And_NumExprOpAnd_IntFnOp; } - export interface GenExprFor_NumValueFor_Decimal256And_NumExprOpAnd_DecimalFnOpAnd_NumOp { - left: NumValueFor_Decimal256And_NumExprOpAnd_DecimalFnOp; - op: NumOp; - right: NumValueFor_Decimal256And_NumExprOpAnd_DecimalFnOp; - } export interface NumExprValueFor_Decimal256And_NumExprOpAnd_DecimalFnOp { left: NumValueFor_Decimal256And_NumExprOpAnd_DecimalFnOp; op: NumExprOp; @@ -314,24 +275,9 @@ export module warp_templates { op: DecimalFnOp; right: NumValueFor_Decimal256And_NumExprOpAnd_DecimalFnOp; } - export interface TimeExpr { - comparator: Uint64; - op: TimeOp; - } - export interface BlockExpr { - comparator: Uint64; - op: NumOp; - } - export interface StaticVariable { - encode: boolean; - kind: VariableKind; - name: string; - update_fn?: UpdateFn | null; - value: string; - } export interface UpdateFn { - on_error?: UpdateFnValue | null; - on_success?: UpdateFnValue | null; + on_error?: FnValue | null; + on_success?: FnValue | null; } export interface ExternalVariable { encode: boolean; @@ -384,10 +330,9 @@ export module warp_templates { templates: Template[]; } export interface Template { - condition?: Condition | null; + executions: Execution[]; formatted_str: string; id: Uint64; - msg: string; name: string; owner: Addr; vars: Variable[]; diff --git a/src/types/job.ts b/src/types/job.ts index a19d8ae..32732b8 100644 --- a/src/types/job.ts +++ b/src/types/job.ts @@ -1,9 +1,13 @@ import { warp_controller, warp_resolver } from './contracts'; -export type Job = Omit & { - vars: warp_resolver.Variable[]; +export type Execution = { condition: warp_resolver.Condition; - msgs: warp_resolver.CosmosMsgFor_Empty[]; + msgs: warp_resolver.WarpMsg[]; +}; + +export type Job = Omit & { + executions: Execution[]; + vars: warp_resolver.Variable[]; }; export type JobResponse = { @@ -15,12 +19,18 @@ export type JobsResponse = { jobs: Job[]; }; +export const parseExecution = (execution: warp_controller.Execution): Execution => { + return { + msgs: JSON.parse(execution.msgs) as warp_resolver.WarpMsg[], + condition: JSON.parse(execution.condition) as warp_resolver.Condition, + }; +}; + export const parseJob = (job: warp_controller.Job): Job => { return { ...job, vars: JSON.parse(job.vars) as warp_resolver.Variable[], - condition: JSON.parse(job.condition) as warp_resolver.Condition, - msgs: JSON.parse(job.msgs) as warp_resolver.CosmosMsgFor_Empty[], + executions: job.executions.map(parseExecution), }; }; diff --git a/src/utils/big.ts b/src/utils/big.ts new file mode 100644 index 0000000..b976bea --- /dev/null +++ b/src/utils/big.ts @@ -0,0 +1,9 @@ +import Big from 'big.js'; + +export function max(a: Big, b: Big): Big { + return a.cmp(b) >= 0 ? a : b; +} + +export function min(a: Big, b: Big): Big { + return a.cmp(b) <= 0 ? a : b; +} diff --git a/src/utils/contract.ts b/src/utils/contract.ts index e91ac91..a1a5563 100644 --- a/src/utils/contract.ts +++ b/src/utils/contract.ts @@ -15,6 +15,13 @@ export type TransferMsg = { }; }; +export type TransferNftMsg = { + transfer_nft: { + recipient: string; + token_id: string; + }; +}; + export const base64encode = (input: any): string => { return Buffer.from(JSON.stringify(JSON.parse(JSON.stringify(input)))).toString('base64'); }; diff --git a/src/utils/fee.ts b/src/utils/fee.ts new file mode 100644 index 0000000..3cb494d --- /dev/null +++ b/src/utils/fee.ts @@ -0,0 +1,57 @@ +import Big from 'big.js'; +import { warp_controller } from '../types'; + +export function computeCreationFee(queueSize: Big, config: warp_controller.Config): Big { + const x1 = Big(config.queue_size_left); + const y1 = Big(config.creation_fee_min); + const x2 = Big(config.queue_size_right); + const y2 = Big(config.creation_fee_max); + + const slope = y2.minus(y1).div(x2.minus(x1)); + + if (queueSize.lt(x1)) { + return y1; + } else if (queueSize.lt(x2)) { + return slope.times(queueSize).plus(y1.minus(slope.times(x1))); + } else { + return y2; + } +} + +export function computeMaintenanceFee(durationDays: Big, config: warp_controller.Config): Big { + const x1 = Big(config.duration_days_min); + const y1 = Big(config.maintenance_fee_min); + const x2 = Big(config.duration_days_max); + const y2 = Big(config.maintenance_fee_max); + + const slope = y2.minus(y1).div(x2.minus(x1)); + + if (durationDays.lt(x1)) { + return y1; + } else if (durationDays.lt(x2)) { + return slope.times(durationDays).plus(y1.minus(slope.times(x1))); + } else { + return y2; + } +} + +export function computeBurnFee(jobReward: Big, config: warp_controller.Config): Big { + const minFee: Big = Big(config.burn_fee_min); + const calculatedFee = jobReward.times(config.burn_fee_rate).div(100); + + if (calculatedFee.gt(minFee)) { + return calculatedFee; + } else { + return minFee; + } +} + +export function calculateDurationDaysAdjustmentFactor(durationDays: Big): Big { + if (durationDays.lte(7)) { + return Big(1); + } else if (durationDays.gte(90)) { + return Big(2); + } + + return Big(1).add(durationDays.sub(7).mul(Big(1).div(83))); +} diff --git a/src/utils/index.ts b/src/utils/index.ts index 42b6cdb..1170637 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,3 +1,4 @@ export * from './contract'; export * from './token'; export * from './mapping'; +export * from './fee'; diff --git a/src/utils/token.ts b/src/utils/token.ts index e145542..1357eb8 100644 --- a/src/utils/token.ts +++ b/src/utils/token.ts @@ -34,6 +34,45 @@ export type IBCToken = TokenBase & { export type Token = NativeToken | CW20Token | IBCToken; +export const LUNA: NativeToken = { + key: 'uluna', + type: 'native', + denom: 'uluna', + name: 'LUNA', + symbol: 'LUNA', + decimals: 6, + icon: 'https://assets.terra.dev/icon/svg/LUNA.png', + coinGeckoId: 'terra-luna-2', +}; + +export const NEUTRON: NativeToken = { + key: 'untrn', + type: 'native', + denom: 'untrn', + name: 'Neutron', + symbol: 'NTRN', + decimals: 6, + icon: 'https://assets.terra.dev/icon/svg/ibc/ATOM.svg', + coinGeckoId: 'neutron', +}; + +export const INJ: NativeToken = { + key: 'inj', + type: 'native', + denom: 'inj', + name: 'Injective', + symbol: 'INJ', + decimals: 18, + icon: 'https://assets.terra.dev/icon/svg/ibc/ATOM.svg', + coinGeckoId: 'injective-protocol', +}; + +export const NATIVE_TOKENS = { + LUNA, + INJ, + NEUTRON, +}; + type Explorer = { address: string; tx: string;