diff --git a/docs/guides/core/node.md b/docs/guides/core/node.md index 1325f050..55411861 100644 --- a/docs/guides/core/node.md +++ b/docs/guides/core/node.md @@ -56,3 +56,14 @@ const txBuilder = new TxBuilderLucidV3( // Do your swap like normal. const result = await txBuilder.swap({ ...args }); ``` + +### Query Providers + +To access a QueryProvider, use this: + +```ts +import { QueryProviderSundaeSwap } from "@sundaeswap/core"; +const queryProvider = new QueryProviderSundaeSwap("preview"); +const ident = "...uniqueIdent..."; +const result = await queryProvider.findPoolData({ ident }); +``` diff --git a/packages/core/src/@types/configs.ts b/packages/core/src/@types/configs.ts index cbaa41d6..4ebfdda0 100644 --- a/packages/core/src/@types/configs.ts +++ b/packages/core/src/@types/configs.ts @@ -91,6 +91,16 @@ export interface IZapConfigArgs extends IOrderConfigArgs { swapSlippage?: number; } +/** + * The arguments configuration for building a valid Strategy. + */ +export interface IStrategyConfigArgs extends IOrderConfigArgs { + suppliedAssets: AssetAmount[]; + orderAddresses: TOrderAddresses; + ownerPublicKey: string; + ownerAddress?: string; +} + /** * The arguments configuration for building a valid Withdraw. */ diff --git a/packages/core/src/Configs/StrategyConfig.class.ts b/packages/core/src/Configs/StrategyConfig.class.ts new file mode 100644 index 00000000..3a299b23 --- /dev/null +++ b/packages/core/src/Configs/StrategyConfig.class.ts @@ -0,0 +1,67 @@ +import { AssetAmount, IAssetAmountMetadata } from "@sundaeswap/asset"; + +import { + IPoolData, + IStrategyConfigArgs, + TOrderAddresses, +} from "../@types/index.js"; +import { Config } from "../Abstracts/Config.abstract.class.js"; +import { OrderConfig } from "../Abstracts/OrderConfig.abstract.class.js"; + +/** + * The main config class for building valid arguments for listing a strategy order. + */ +export class StrategyConfig extends OrderConfig { + suppliedAssets?: AssetAmount[]; + + ownerPublicKey?: string; + + constructor(args?: IStrategyConfigArgs) { + super(); + + args && this.setFromObject(args); + } + + setSuppliedAssets(assets: AssetAmount[]) { + this.suppliedAssets = assets; + return this; + } + + setOwnerPublicKey(publicKey: string) { + this.ownerPublicKey = publicKey; + return this; + } + + buildArgs(): IStrategyConfigArgs { + this.validate(); + + return { + pool: this.pool as IPoolData, + orderAddresses: this.orderAddresses as TOrderAddresses, + ownerPublicKey: this.ownerPublicKey as string, + suppliedAssets: this + .suppliedAssets as AssetAmount[], + referralFee: this.referralFee, + }; + } + + setFromObject({ + orderAddresses, + suppliedAssets, + referralFee, + }: IStrategyConfigArgs): void { + this.setOrderAddresses(orderAddresses); + this.setSuppliedAssets(suppliedAssets); + referralFee && this.setReferralFee(referralFee); + } + + validate(): never | void { + super.validate(); + + if (!this.suppliedAssets) { + throw new Error( + "You did not provided funding for this listed strategy! Make sure you supply the necessary assets with .setSuppliedAssets()" + ); + } + } +} diff --git a/packages/core/src/DatumBuilders/DatumBuilder.Lucid.V3.class.ts b/packages/core/src/DatumBuilders/DatumBuilder.Lucid.V3.class.ts index f0009bee..74e86469 100644 --- a/packages/core/src/DatumBuilders/DatumBuilder.Lucid.V3.class.ts +++ b/packages/core/src/DatumBuilders/DatumBuilder.Lucid.V3.class.ts @@ -25,6 +25,14 @@ export interface IDatumBuilderBaseV3Args { scooperFee: bigint; } +/** + * The arguments from building a strategy transaction against + * a V3 pool contract. + */ +export interface IDatumBuilderStrategyV3Args extends IDatumBuilderBaseV3Args { + ownerPublicKey: string; +} + /** * The arguments from building a swap transaction against * a V3 pool contract. @@ -283,6 +291,38 @@ export class DatumBuilderLucidV3 implements DatumBuilder { }; } + buildStrategyDatum({ + destinationAddress, + ident, + ownerAddress, + ownerPublicKey, + scooperFee, + }: IDatumBuilderStrategyV3Args): TDatumResult { + const strategyDatum: V3Types.TOrderDatum = { + destination: this.buildDestinationAddresses(destinationAddress).schema, + extension: Data.void(), + order: { + Strategy: { + auth: { + Signature: { + bytes: ownerPublicKey, + }, + }, + }, + }, + owner: this.buildOwnerDatum(ownerAddress ?? destinationAddress.address) + .schema, + poolIdent: this.buildPoolIdent(ident), + scooperFee: scooperFee, + }; + const inline = Data.to(strategyDatum, V3Types.OrderDatum); + return { + hash: LucidHelper.inlineDatumToHash(inline), + inline, + schema: strategyDatum, + }; + } + /** * Creates a redeemer datum for minting a new pool. This is attached to the new assets that * creating a new pool mints on the blockchain. See {@link Lucid.TxBuilderLucidV3} for more diff --git a/packages/core/src/DatumBuilders/contracts/contracts.v3.ts b/packages/core/src/DatumBuilders/contracts/contracts.v3.ts index 0a176394..9d706310 100644 --- a/packages/core/src/DatumBuilders/contracts/contracts.v3.ts +++ b/packages/core/src/DatumBuilders/contracts/contracts.v3.ts @@ -88,6 +88,15 @@ export type TSingletonValue = Data.Static; export const SingletonValue = SingletonValueSchema as unknown as TSingletonValue; +export const StrategyAuthorizationSchema = Data.Enum([ + Data.Object({ Signature: Data.Object({ bytes: Data.Bytes() }) }), + Data.Object({ Script: Data.Object({ bytes: Data.Bytes() }) }), +]); + +export const StrategySchema = Data.Object({ + auth: StrategyAuthorizationSchema, +}); + export const SwapSchema = Data.Object({ offer: SingletonValueSchema, minReceived: SingletonValueSchema, @@ -108,7 +117,7 @@ export const DonationSchema = Data.Object({ }); export const OrderSchema = Data.Enum([ - Data.Object({ Strategies: Data.Nullable(Data.Literal("TODO")) }), + Data.Object({ Strategy: StrategySchema }), Data.Object({ Swap: SwapSchema }), Data.Object({ Deposit: DepositSchema }), Data.Object({ Withdrawal: WithdrawalSchema }), diff --git a/packages/core/src/TxBuilders/TxBuilder.Lucid.V3.class.ts b/packages/core/src/TxBuilders/TxBuilder.Lucid.V3.class.ts index 3bc24df4..e51d5df5 100644 --- a/packages/core/src/TxBuilders/TxBuilder.Lucid.V3.class.ts +++ b/packages/core/src/TxBuilders/TxBuilder.Lucid.V3.class.ts @@ -18,6 +18,7 @@ import type { IDepositConfigArgs, IMintV3PoolConfigArgs, IOrderRouteSwapArgs, + IStrategyConfigArgs, ISundaeProtocolParamsFull, ISundaeProtocolReference, ISundaeProtocolValidatorFull, @@ -34,6 +35,7 @@ import { TxBuilder } from "../Abstracts/TxBuilder.abstract.class.js"; import { CancelConfig } from "../Configs/CancelConfig.class.js"; import { DepositConfig } from "../Configs/DepositConfig.class.js"; import { MintV3PoolConfig } from "../Configs/MintV3PoolConfig.class.js"; +import { StrategyConfig } from "../Configs/StrategyConfig.class.js"; import { SwapConfig } from "../Configs/SwapConfig.class.js"; import { WithdrawConfig } from "../Configs/WithdrawConfig.class.js"; import { ZapConfig } from "../Configs/ZapConfig.class.js"; @@ -1145,6 +1147,50 @@ export class TxBuilderLucidV3 extends TxBuilder { return sortedUtxos; } + async strategy(args: IStrategyConfigArgs) { + // The difference between orderAddresses and ownerAddress is: + // orderAddresses tell you where the result is going next (possibly chained). + // the ownerAddress tells you who should always be able to cancel the order at any step. + + const { + pool, + orderAddresses, + suppliedAssets, + ownerAddress, + ownerPublicKey, + referralFee, + } = new StrategyConfig(args).buildArgs(); + + const { inline } = this.datumBuilder.buildStrategyDatum({ + ident: pool.ident, + destinationAddress: orderAddresses.DestinationAddress, + ownerPublicKey: args.ownerPublicKey, + scooperFee: await this.getMaxScooperFeeAmount(), + }); + + const tx = this.newTxInstance(referralFee); + + const payment = SundaeUtils.accumulateSuppliedAssets({ + scooperFee: await this.getMaxScooperFeeAmount(), + suppliedAssets, + }); + + tx.payToContract( + // This might need to be a different script address, not sure. + await this.generateScriptAddress( + "order.spend", + orderAddresses.DestinationAddress.address + ), + { inline }, + payment + ); + + return this.completeTx({ + tx, + datum: inline, + }); + } + private async completeTx({ tx, datum,