-
Notifications
You must be signed in to change notification settings - Fork 0
(2) Settler: Intent lifecycle #48
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: solana/1-whitelist
Are you sure you want to change the base?
(2) Settler: Intent lifecycle #48
Conversation
PedroAraoz
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would update the imports to make them a bit shorter and reduce verbosity
from: Box<Account<'info, crate::whitelist::accounts::GlobalSettings>>,
to: Box<Account<'info, WhitelistSettings>>,
the same with the errors as we discussed offline.
… plus other suggestions
| @@ -0,0 +1,4 @@ | |||
| pub const DEPLOYER_KEY: &'static str = env!( | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| pub const DEPLOYER_KEY: &'static str = env!( | |
| pub const DEPLOYER_KEY: &str = env!( |
| #[account( | ||
| seeds = [b"entity-registry", &[EntityType::Solver as u8 + 1], solver.key().as_ref()], | ||
| bump = solver_registry.bump, | ||
| seeds::program = crate::controller::ID |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| seeds::program = crate::controller::ID | |
| seeds::program = controller::ID |
|
|
||
| let settler_settings = &mut ctx.accounts.settler_settings; | ||
|
|
||
| settler_settings.controller_program = crate::controller::ID; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| settler_settings.controller_program = crate::controller::ID; | |
| settler_settings.controller_program = controller::ID; |
| bump | ||
| )] | ||
| /// This PDA must be uninitialized | ||
| pub fulfilled_intent: SystemAccount<'info>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
are we doing something with this account?
| export function generateIntentHash(): string { | ||
| return Buffer.from(Array.from({ length: INTENT_HASH_LENGTH }, () => Math.floor(Math.random() * 256))).toString('hex') | ||
| } | ||
|
|
||
| /** | ||
| * Generate a random 32-byte hex string for nonce | ||
| */ | ||
| export function generateNonce(): string { | ||
| return Buffer.from(Array.from({ length: NONCE_LENGTH }, () => Math.floor(Math.random() * 256))).toString('hex') | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use randomHex() from the sdk
| /** | ||
| * Create a validated intent (with validations set to meet min_validations requirement) | ||
| */ | ||
| export async function createValidatedIntent( | ||
| solverSdk: SettlerSDK, | ||
| solverProvider: LiteSVMProvider, | ||
| client: LiteSVM, | ||
| options: { | ||
| intentHash?: string | ||
| minValidations?: number | ||
| isFinal?: boolean | ||
| deadline?: number | ||
| } = {} | ||
| ): Promise<string> { | ||
| const intentHash = await createTestIntent(solverSdk, solverProvider, { | ||
| ...options, | ||
| isFinal: options.isFinal ?? true, | ||
| }) | ||
|
|
||
| // Set validations to meet min_validations requirement | ||
| const intentKey = solverSdk.getIntentKey(intentHash) | ||
| const intentAccount = client.getAccount(intentKey) | ||
| if (intentAccount) { | ||
| const intentData = Buffer.from(intentAccount.data) | ||
| // validations is at offset: 8 (disc) + 1 (op) + 32 (user) + 32 (intent_creator) + 32 (intent_hash) + 32 (nonce) + 8 (deadline) + 2 (min_validations) = 147 | ||
| // validations is u16, so 2 bytes | ||
| const minValidations = options.minValidations ?? DEFAULT_MIN_VALIDATIONS | ||
| intentData.writeUInt16LE(minValidations, 147) | ||
| client.setAccount(intentKey, { | ||
| ...intentAccount, | ||
| data: intentData, | ||
| }) | ||
| } | ||
|
|
||
| return intentHash | ||
| } | ||
|
|
||
| /** | ||
| * Creates an allowlisted entity (validator, axia, or solver) | ||
| */ | ||
| export async function createAllowlistedEntity( | ||
| controllerSdk: ControllerSDK, | ||
| provider: LiteSVMProvider, | ||
| entityType: EntityType, | ||
| entityKeypair?: Keypair | ||
| ): Promise<Keypair> { | ||
| const entity = entityKeypair || Keypair.generate() | ||
| const allowlistIx = await controllerSdk.createEntityRegistryIx(entityType, entity.publicKey) | ||
| await makeTxSignAndSend(provider, allowlistIx) | ||
| return entity | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| /** | |
| * Create a validated intent (with validations set to meet min_validations requirement) | |
| */ | |
| export async function createValidatedIntent( | |
| solverSdk: SettlerSDK, | |
| solverProvider: LiteSVMProvider, | |
| client: LiteSVM, | |
| options: { | |
| intentHash?: string | |
| minValidations?: number | |
| isFinal?: boolean | |
| deadline?: number | |
| } = {} | |
| ): Promise<string> { | |
| const intentHash = await createTestIntent(solverSdk, solverProvider, { | |
| ...options, | |
| isFinal: options.isFinal ?? true, | |
| }) | |
| // Set validations to meet min_validations requirement | |
| const intentKey = solverSdk.getIntentKey(intentHash) | |
| const intentAccount = client.getAccount(intentKey) | |
| if (intentAccount) { | |
| const intentData = Buffer.from(intentAccount.data) | |
| // validations is at offset: 8 (disc) + 1 (op) + 32 (user) + 32 (intent_creator) + 32 (intent_hash) + 32 (nonce) + 8 (deadline) + 2 (min_validations) = 147 | |
| // validations is u16, so 2 bytes | |
| const minValidations = options.minValidations ?? DEFAULT_MIN_VALIDATIONS | |
| intentData.writeUInt16LE(minValidations, 147) | |
| client.setAccount(intentKey, { | |
| ...intentAccount, | |
| data: intentData, | |
| }) | |
| } | |
| return intentHash | |
| } | |
| /** | |
| * Creates an allowlisted entity (validator, axia, or solver) | |
| */ | |
| export async function createAllowlistedEntity( | |
| controllerSdk: ControllerSDK, | |
| provider: LiteSVMProvider, | |
| entityType: EntityType, | |
| entityKeypair?: Keypair | |
| ): Promise<Keypair> { | |
| const entity = entityKeypair || Keypair.generate() | |
| const allowlistIx = await controllerSdk.createEntityRegistryIx(entityType, entity.publicKey) | |
| await makeTxSignAndSend(provider, allowlistIx) | |
| return entity | |
| } |
If you don't mind deleting this as it is not used yet. I want to review it when it's used
Adds Settler program, with instructions pertinent to the lifecycle of an
IntentPDA.Includes Rust program, Typescript SDK, and Typescript tests.
Instructions
Following are instructions for creation, extension, and deletion of an
Intent.Additionally, the
initializeandset_paused_stateinstructions were added to manage the Settler program's global state.initializeSets global state, defining the Whitelist program to be used and the paused state.
This program doesn't have an admin.
set_paused_statePauses or unpauses program. The admin of the defined Whitelist program can execute this action.
create_intentInitializes an
IntentPDA with all its fields.NOTE: Some of these fields might not be necessary to have on-chain (or a hash might be enough). I have simply added them all until the Settler's first iteration is done and I can remove what is not necessary, as it is easier to remove than to add here. For now, we are assuming that we need all the fields.
Technical debt: Check that the Intent hash is correct.
extend_intentAdds more
data,max_fees, oreventsto anIntentPDA in case we ran out of space in thecreate_intentcall.Solana transactions have a limited size of
1232bytes, so we can only pass in that many bytes (even less since there's other overhead) as instruction parameters. For this reason,Intents have an intermediate state withis_final=falsewhere they can still be extended by theintent_creatorwith more data added in further transactions, non-atomically.claim_stale_intentIf an
Intentexpired (deadline is in the past), theintent_creatorcan close the PDA and reclaim the rent, as this account is now useless.State
IntentPDA defining an Intent with all its fields.
NOTE: This account will be reduced in a future iteration, with fields that aren't necessary on-chain being removed.
FulfilledIntentA minimal on-chain proof that an
Intentexisted and was executed, to save as much rent as possible.When an
Intentis executed, it is closed, rent goes back to the creator, and this PDA is initialized in its place.SettlerSettingsGlobal state of the program.
NOTE: Some files might have content from future PRs given this series of PRs come from a larger PR that was split (#41). I have tried to keep this to a minimum but bear this in mind.