DTA Subscriptions and Redemptions
This page covers the SDK calls for the investor-facing DTA flows, requestSubscription, requestRedemption, and cancelDistributorRequest, and the fund-admin operations that drive a request through to settlement (processDistributorRequest, completeRequestProcessing).
Prerequisites
Before any subscription or redemption can succeed, the actors and fund must be set up per the DTA standard. In SDK terms:
- The fund admin must be registered (
PrepareRegisterFundAdminOperation; see Fund & Distributor Management). - The fund token must be registered and enabled.
- The distributor must be registered and authorised for the fund token.
You also need the extension constructed:
import (
dtaop "github.com/smartcontractkit/crec-sdk-ext-dta/v2/operations"
)
ext, err := dtaop.New(&dtaop.Options{
AccountAddress: smartAccount.Hex(),
DTARequestManagementAddress: mgmtAddr.Hex(),
DTARequestSettlementAddress: settleAddr.Hex(),
})
Request a subscription
Two flavours: with and without an inline token approval.
Without approval (token already approved)
op, err := ext.PrepareRequestSubscriptionOperation(
fundAdminAddr,
fundTokenId, // [32]byte
amount, // *big.Int: payment-token units
referenceID, // [32]byte: your idempotency key
)
if err != nil { return err }
opr, err := client.Transact.ExecuteOperation(ctx, channelID, signer, op, chainSelector)
Underlying call:
DTARequestManagement.requestSubscription(fundAdminAddr, fundTokenId, amount, referenceID)
Use this when the investor's Smart Account has previously approved the management contract for amount.
With inline approve (recommended)
For first-time subscriptions or whenever the existing allowance is insufficient, batch approve and requestSubscription into a single atomic operation:
op, err := ext.PrepareRequestSubscriptionWithTokenApprovalOperation(
fundAdminAddr,
fundTokenId,
amount,
referenceID,
paymentTokenAddress, // ERC-20 used for payment (e.g. USDC)
)
The returned operation contains two transactions:
paymentToken.approve(managementAddr, amount)DTARequestManagement.requestSubscription(fundAdminAddr, fundTokenId, amount, referenceID)
Either both succeed atomically or neither does; see Batch Multiple Transactions.
Resulting events
A successful requestSubscription emits one SubscriptionRequested event:
type SubscriptionRequested struct {
FundAdminAddr common.Address
FundTokenId common.Hash
DistributorAddr common.Address
ReferenceID common.Hash
RequestId common.Hash
Amount *big.Int
CreatedAt uint64
}
Persist RequestId: it threads through the entire request lifecycle.
Request a redemption
op, err := ext.PrepareRequestRedemptionOperation(
fundAdminAddr,
fundTokenId,
shares, // *big.Int: fund-token units
referenceID,
)
Underlying call:
DTARequestManagement.requestRedemption(fundAdminAddr, fundTokenId, shares, referenceID)
The Smart Account must already hold (or be approved for) the fund tokens being redeemed. Build a separate batched operation if you need to combine approve(fundToken, mgmt, shares) + requestRedemption(...).
Resulting events
type RedemptionRequested struct {
FundAdminAddr common.Address
FundTokenId common.Hash
DistributorAddr common.Address
ReferenceID common.Hash
RequestId common.Hash
Shares *big.Int
CreatedAt uint64
}
Cancel a request
Investors (or the distributor on their behalf) can cancel a request before it is picked up:
op, err := ext.PrepareCancelDistributorRequestOperation(requestId)
Underlying call: DTARequestManagement.cancelDistributorRequest(requestId). Emits DistributorRequestCanceled:
type DistributorRequestCanceled struct {
FundAdminAddr common.Address
FundTokenId common.Hash
DistributorAddr common.Address
RequestId common.Hash
}
A cancelled request cannot be reopened: re-issue with a fresh referenceID if needed.
Process a request (fund admin)
The fund admin's worker picks up an open request:
op, err := ext.PrepareProcessDistributorRequestOperation(requestId)
Underlying call: DTARequestManagement.processDistributorRequest(requestId). Emits DistributorRequestProcessing:
type DistributorRequestProcessing struct {
FundAdminAddr common.Address
FundTokenId common.Hash
DistributorAddr common.Address
RequestId common.Hash
Shares *big.Int
Amount *big.Int
}
This event is enriched with on-chain reference data (distributor_request, fund_token_data); see DTA Events.
Complete a request (settlement)
After settlement processing succeeds (or fails) on the fund side:
op, err := ext.PrepareCompleteRequestProcessingOperation(
requestId,
success, // bool: true if shares were minted / payment was made
errBytes, // []byte: abi-encoded revert reason if !success
revertOnErr, // bool: true to bubble error up to the caller
)
Underlying call: DTARequestSettlement.completeRequestProcessing(requestId, success, err, revertOnErr). Emits DistributorRequestProcessed:
type DistributorRequestProcessed struct {
RequestId common.Hash
Shares *big.Int
Status RequestStatus
Error []byte
}
Status is the DTA request state machine, surfaced from the Solidity enum as a Go uint8. The SDK exposes named constants for every value:
| Constant | Standard state |
|---|---|
RequestStatusNone | Zero value (request not yet recorded) |
RequestStatusPending | Pending |
RequestStatusProcessing | Processing |
RequestStatusProcessed | Processed |
RequestStatusCanceled | Canceled |
RequestStatusFailed | Failed |
For the full state diagram and NAV-TTL behavior (manual vs automatic processing), see the request lifecycle page.
Where the SDK fits in the standard's flow
The DTA standard describes a four-step subscription flow (Request Submission → NAV Update → Request Processing → Token Minting & Escrow → Settlement). The CRE Connect SDK is the submission and observation layer for that flow:
| Standard step | SDK call | Resulting event (decoded via dtav2.DecodeFromEvent) |
|---|---|---|
| Request Submission | PrepareRequestSubscriptionWithTokenApprovalOperation + ExecuteOperation | SubscriptionRequested |
| Request Processing (admin) | PrepareProcessDistributorRequestOperation + ExecuteOperation | DistributorRequestProcessing |
| Settlement completion | PrepareCompleteRequestProcessingOperation + ExecuteOperation | DistributorRequestProcessed (+ DTASettlementOpened / DTASettlementClosed for cross-chain) |
Subscribe to these events via the dta.v2 service (Service: "dta.v2" on Watchers.CreateWithService); see DTA Events for every payload shape.
Idempotency with referenceID
Always pass a stable referenceID (a 32-byte hash of your client-side request ID). The contract uses it to detect duplicates and to surface the chain-side RequestId back to your application via the emitted event.
import "github.com/ethereum/go-ethereum/crypto"
referenceID := crypto.Keccak256Hash([]byte(yourInternalRequestUUID))
var refArr [32]byte
copy(refArr[:], referenceID.Bytes())
Next steps
- Fund & Distributor Management: set up admins, tokens, and distributors before subscribing.
- DTA Events: full event reference, including settlement events.
- Submit and Track Operations: drive the operation through to
confirmed.