This guide demonstrates how to build a custom integration program that interacts with GLAM Protocol through Cross-Program Invocation (CPI). The example program adds support of Drift Protocol's transfer_deposit instruction.
For demo purpose, we use the mainnet staging program (
gstgptmbgJVi5f8ZmSRVZjZkDQwqKa3xWuUtD5WmJHz) of GLAM.
Add GLAM Protocol and target protocol dependencies to Cargo.toml:
[dependencies]
anchor-lang = { workspace = true }
glam_protocol = { path = "../../deps/glam_protocol" }
drift = { path = "../../deps/drift" } # Replace with your target protocolglam_protocol and drift are auto generated crates from IDLs using anchor-gen. They provide helpful CPI interfaces that make the integration process easier.
Create instructions that wrap target protocol operations. Each instruction should:
- Accept required parameters - Include instruction-specific parameters (e.g.,
market_index,amount) - Validate authorization - Check integration and signer permissions
- Build instruction payload - Construct the instruction data for the target protocol
- Prepare remaining accounts - Collect all accounts needed by the target protocol
- Call CPI proxy - Invoke GLAM Protocol's
cpi_proxyto execute the instruction
See lib.rs:18-79 for a complete example.
Define account structures that include:
-
GLAM accounts (required):
glam_state: The vault state accountglam_vault: The vault PDA (derived with seeds["vault", glam_state])glam_signer: The transaction signerglam_protocol_program: GLAM Protocol program
-
Integration authority (required):
- PDA with seeds
["integration-authority"]from the integration program
- PDA with seeds
-
Target protocol accounts:
cpi_program: The target protocol program- Protocol-specific accounts (mark as
UncheckedAccountif validated by target protocol)
See lib.rs:82-112 for the account structure.
Authorization ensures only permitted users can execute specific operations. This is implemented in acl.rs.
Use bitflags to represent protocols your integration supports:
#[repr(u16)]
pub enum SupportedProtocols {
DriftProtocol = PROTO_DRIFT, // 1 << 0
}
#[constant]
pub const PROTO_DRIFT: u16 = 1 << 0;Define granular permissions for each protocol:
#[repr(u64)]
pub enum DriftPermissions {
DriftTransferDeposit = PROTO_DRIFT_TRANSFER_DEPOSIT, // 1 << 0
}
#[constant]
pub const PROTO_DRIFT_TRANSFER_DEPOSIT: u64 = 1 << 0;Verify the protocol is enabled for the vault (see acl.rs:45-63):
check_integration_and_protocol(
&ctx.accounts.glam_state,
&crate::ID,
SupportedProtocols::DriftProtocol.into(),
)?;This checks if the integration program and protocol are registered in the vault's integration_acls.
Verify the signer has permission for the specific operation (see acl.rs:66-107):
check_signer_access(
&ctx.accounts.glam_state,
ctx.accounts.glam_signer.key,
&crate::ID,
SupportedProtocols::DriftProtocol.into(),
DriftPermissions::DriftTransferDeposit.into(),
)?;This checks:
- Owner has full access
- Delegates must have explicit permission for this integration/protocol/operation
- Delegate access must not be expired
The CPI proxy is GLAM's mechanism for secure cross-program invocations. Your integration program calls glam_protocol::cpi::cpi_proxy which:
- Validates the integration authority - Ensures your program is authorized
- Uses the vault's authority - Signs transactions with the vault's PDA
- Executes the target instruction - Invokes the target protocol with provided payload and accounts