DTA Fund and Distributor Management

This page covers the operator-side flows of DTA v2: onboarding fund admins, registering fund tokens, managing distributors, and wiring cross-DTA settlement. Investor-facing flows (subscriptions, redemptions) live in Subscriptions and Redemptions.

All examples assume an extension constructed as in DTA Overview:

ext, _ := dtaop.New(&dtaop.Options{
    AccountAddress:              smartAccount.Hex(),
    DTARequestManagementAddress: mgmtAddr.Hex(),
    DTARequestSettlementAddress: settleAddr.Hex(),
})

Fund admin onboarding

Register once per Smart Account that will act as a Fund Administrator:

op, err := ext.PrepareRegisterFundAdminOperation()

Underlying call: DTARequestManagement.registerFundAdmin(). Emits FundAdminRegistered:

type FundAdminRegistered struct {
    FundAdminAddr common.Address
}

Register a fund token

Each tokenised fund is registered once with its complete configuration:

import dtaevents "github.com/smartcontractkit/crec-sdk-ext-dta/v2/events"

tokenData := dtaevents.FundTokenData{
    FundTokenAddr:                fundTokenAddr,
    NavFeedDecimals:              8,
    PurchaseTokenRoundingDecimals: 6,
    PurchaseTokenDecimals:        6,    // e.g. USDC
    FundRoundingDecimals:         18,
    FundTokenDecimals:            18,
    RequestsPerDay:               4,
    NavAddr:                      navOracleAddr,
    TokenChainSelector:           5009297550715157269, // Ethereum mainnet
    DtaRequestSettlementAddr:     settleAddr,
    TimezoneOffsetSecs:           big.NewInt(0),
    NavTTL:                       big.NewInt(86400),
    PaymentInfo: dtaevents.DTAPayment{
        OffChainPaymentCurrency: 0,
        PaymentTokenSourceAddr:  paymentTokenAddr,
        PaymentTokenDestAddr:    paymentDestAddr,
    },
}

op, err := ext.PrepareRegisterFundTokenOperation(fundTokenId, tokenData)

Underlying call: DTARequestManagement.registerFundToken(fundTokenId, tokenData). Emits FundTokenRegistered:

type FundTokenRegistered struct {
    FundAdminAddr      common.Address
    FundTokenId        common.Hash
    FundTokenAddr      common.Address
    NavAddr            common.Address
    TokenChainSelector uint64
}

fundTokenId is the Fund Token ID, a [32]byte (bytes32) identifier defined by the DTA Technical Standard. Choose a derivation strategy that fits your fund-token registry; see the DTA Standard glossary for the canonical definition.

Toggle availability

A fund token can be temporarily disabled (rejecting new requests) without re-registering:

op, _ := ext.PrepareDisableFundTokenOperation(fundTokenId)
op, _ := ext.PrepareEnableFundTokenOperation(fundTokenId)

Underlying calls: disableFundToken(fundTokenId) / enableFundToken(fundTokenId). Both emit no separate event; observe via the on-chain reference data.

Distributor lifecycle

A Fund Distributor is a Smart Account authorised to submit subscription / redemption requests on behalf of investors.

Register a distributor

op, err := ext.PrepareRegisterDistributorOperation(distributorWalletAddr)

Underlying call: DTARequestManagement.registerDistributor(distributorWalletAddr). Emits DistributorRegistered:

type DistributorRegistered struct {
    DistributorAddr common.Address
}

Authorise / revoke a distributor for a fund token (v2 model)

DTA v2 uses an authorise / revoke model rather than the v1 allow / disallow flag. The fund admin signs authorize or revoke calls to set per-token distributor permissions:

op, _ := ext.PrepareAuthorizeDistributorForTokenOperation(fundAdminAddr, fundTokenId, distributorAddr)
op, _ := ext.PrepareRevokeDistributorForTokenOperation(fundAdminAddr, fundTokenId, distributorAddr)

Underlying calls:

  • authorizeDistributorForToken(fundAdminAddr, fundTokenId, distributorAddr)
  • revokeDistributorForToken(fundAdminAddr, fundTokenId, distributorAddr)

Both emit DistributorAuthorizationUpdated:

type DistributorAuthorizationUpdated struct {
    DistributorAddr common.Address
    FundAdminAddr   common.Address
    FundTokenId     common.Hash
    Authorized      bool
}

Allow-list (per-token, no admin involvement)

For lower-trust scenarios where the fund admin already pre-approved a category of distributors, the per-token allow-list is the lightweight knob:

op, _ := ext.PrepareAllowDistributorForTokenOperation(fundTokenId, distributorAddr)
op, _ := ext.PrepareDisallowDistributorForTokenOperation(fundTokenId, distributorAddr)

Emits FundTokenAllowlistUpdated:

type FundTokenAllowlistUpdated struct {
    FundAdminAddr   common.Address
    FundTokenId     common.Hash
    DistributorAddr common.Address
    Allowed         bool
}

Cross-DTA settlement

For cross-chain settlement topologies, configure which remote DTA contracts are allowed to settle against your settlement contract. The Chain Selector identifies the remote chain.

op, _ := ext.PrepareAllowDTAOperation(
    dtaAddr,             // remote DTA contract
    dtaChainSelector,    // CCIP chain selector
    fundAdminAddr,
    fundTokenId,
    fundTokenAddr,
    dtaevents.TokenMintTypeMint,        // or TokenMintTypeIssueTokens
    dtaevents.TokenBurnTypeBurn,        // or BurnFrom / BurnWithReason / ForceBurn
)
op, _ := ext.PrepareDisallowDTAOperation(dtaAddr, dtaChainSelector, fundAdminAddr, fundTokenId)

Emits DTAAdded / DTARemoved:

type DTAAdded struct {
    DtaAddr          common.Address
    DtaChainSelector uint64
    FundAdminAddr    common.Address
    FundTokenId      common.Hash
    FundTokenAddr    common.Address
}

type DTARemoved struct {
    DtaAddr          common.Address
    DtaChainSelector uint64
    FundAdminAddr    common.Address
    FundTokenId      common.Hash
}

Match the TokenMintType / TokenBurnType to the fund token contract's interface; see the v2 events types.go for the full mapping.

Settlement contract operations

Two ownership operations bind to the settlement contract:

op, _ := ext.PrepareTransferDTARequestSettlementOwnershipOperation(newOwner)
op, _ := ext.PrepareRenounceDTARequestSettlementOwnershipOperation()

These are standard OpenZeppelin Ownable operations.

Token withdrawals

Both contracts expose a withdrawTokens operation for sweeping accidentally-sent ERC-20 balances:

op, _ := ext.PrepareWithdrawManagementTokensOperation(token, recipient, amount)
op, _ := ext.PrepareWithdrawSettlementTokensOperation(token, recipient, amount)

Emits TokenWithdrawn:

type TokenWithdrawn struct {
    Token     common.Address
    Recipient common.Address
    Amount    *big.Int
}

CCIP gas-limit tuning

For cross-chain operations, both contracts let the owner adjust the CCIP message gas limit:

op, _ := ext.PrepareSetManagementCCIPGasLimitOperation(big.NewInt(500_000))
op, _ := ext.PrepareSetSettlementCCIPGasLimitOperation(big.NewInt(500_000))

Tune in response to gas-limit-exceeded settlement failures.

Suggested onboarding sequence

1. PrepareRegisterFundAdminOperation                  (fund admin)
2. PrepareRegisterFundTokenOperation                  (fund admin, per token)
3. PrepareEnableFundTokenOperation                    (fund admin)
4. PrepareRegisterDistributorOperation                (distributor)
5. PrepareAuthorizeDistributorForTokenOperation       (fund admin, per (token, distributor))
6. (optionally) PrepareAllowDTAOperation              (cross-chain DTA)
7. Investors can now subscribe / redeem

Next steps

Get the latest Chainlink content straight to your inbox.