CCTP

Sui Packages

CCTP V1 Packages on the Sui blockchain

The CCTP Sui smart contract implementation is written in Move. The Sui CCTP implementation is split into two packages: MessageTransmitter and TokenMessengerMinter. TokenMessengerMinter encapsulates the functionality of both TokenMessenger and TokenMinter contracts on EVM chains. To ensure alignment with EVM contracts logic and state, and to facilitate future upgrades and maintenance, the code and state of the Sui packages reflect the EVM counterparts as closely as possible.

There are a few key differences with Sui packages from EVM and other CCTP implementations:

Since the Move language does not have interfaces, the message_transmitter::receive_message() function cannot call directly into the receiver package (e.g. TokenMessenger for USDC transfers). The workaround for this limitation is that callers of receive_message() must also atomically (in the same Programmable Transaction Block (PTB)) call into the receiver package's handle_receive_message() function with a Receipt struct, call stamp_receipt() with the StampReceiptTicket struct returned from handle_receive_message(), and then pass the StampedReceipt back into the message_transmitter::complete_receive_message() function to complete the message and destroy the Receipt object. This flow ensures atomicity and guarantees message receipt by the receiver packages. Please see the interface and examples below for more information on this flow.

On Sui, when a package is upgraded, the new version is deployed with a new package ID. This means if another package is directly calling a version-gated function, when the package is upgraded, the dependent packages must also be upgraded. To address this, all CCTP functions that are intended to be called from a dependent package follow a Ticket struct pattern. In this pattern, the dependent package can call non version-gated create_ticket() functions with the intended function parameters, including an Auth struct (used to uniquely identify the package), and receive back a Ticket struct. This struct can then be returned from the dependent package and used in a PTB to call the intended CCTP function. This allows integrators to securely integrate with CCTP functions from their packages, and only have to update PTBs when CCTP packages are upgraded rather than having to upgrade their packages as well. For more information, see the functions below with the _with_package_auth suffix.

PackageDomainAddress
MessageTransmitter80x4931e06dce648b3931f890035bd196920770e913e43e45990b383f6486fdd0a5
TokenMessengerMinter80x31cc14d80c175ae39777c0238f20594c6d4869cfab199f40b69f3319956b8beb
ObjectObject ID
MessageTransmitterState0x98234bd0fa9ac12cc0a20a144a22e36d6a32f7e0a97baaeaf9c76cdc6d122d2e
TokenMessengerMinterState0x5252abd1137094ed1db3e0d75bc36abcd287aee4bc310f8e047727ef5682e7c2
USDC Treasury Object0x7170137d4a6431bf83351ac025baf462909bffe2877d87716374fb42b9629ebe

Branch with testnet Automated Address Management: github.com/circlefin/sui-cctp/tree/testnet.

PackageDomainAddress
MessageTransmitter80x08d87d37ba49e785dde270a83f8e979605b03dc552b5548f26fdf2f49bf7ed1b
TokenMessengerMinter80x2aa6c5d56376c371f88a6cc42e852824994993cb9bab8d3e6450cbe3cb32b94e
ObjectObject ID
MessageTransmitterState0xf68268c3d9b1df3215f2439400c1c4ea08ac4ef4bb7d6f3ca6a2a239e17510af
TokenMessengerMinterState0x45993eecc0382f37419864992c12faee2238f5cfe22b98ad3bf455baf65c8a2f
USDC Treasury Object0x57d6725e7a8b49a7b2a612f6bd66ab5f39fc95332ca48be421c3229d514a6de7

Branch with mainnet Automated Address Management: github.com/circlefin/sui-cctp/tree/mainnet.

The Sui CCTP source code is available on GitHub.

The interface below serves as a reference for permissionless messaging functions exposed by the programs.

deposit_for_burn

Burns passed in tokens from sender to be minted on destination domain. Minted tokens will be transferred to mint_recipient on the destination chain. The deposit_for_burn interface and functionality is very similar to the EVM implementation. The coins parameter is the key difference due to how passing tokens around on Sui works. message_transmitter_state, deny_list, and treasury parameters are all shared objects.

Remarks:

  • Intended to be called directly by EOA (rather than a dependent package). The initiating EOA will be the "owner" (e.g. message sender) of the message and have the ability to call replace_deposit_for_burn() to update the mint_recipient or destination_caller. If the calling EOA is not trusted by the mint recipient or destination caller, deposit_for_burn_with_package_auth() should be called instead with the integrating package owning the message.
  • The generic type T is the coin's one-time witness (OTW) type for the specific coin type to be burned.
  • BurnMessage and Message structs are returned, but it is not required to do anything with these structs; they are returned for convenience.

Parameters

FieldTypeDescription
coinsCoin<T>Coin of type T to be burned. Full amount in coins will be burned.
destination_domainu32Destination domain identifier.
mint_recipientaddressAddress of mint recipient on destination domain. Note: If destination is a non-Move chain, mint_recipient address should be converted to hex and passed in using the @0x123 address format.
state&StateShared State object for the TokenMessengerMinter package.
message_transmitter_state&mut MessageTransmitterStateShared State object for the MessageTransmitter package.
deny_list&DenyListDenyList shared object for the stablecoin token T. Constant address: 0x403
treasury&mut Treasury<T>Treasury shared object for the stablecoin token T.
ctx&TxContextTxContext for the transaction.

deposit_for_burn_with_package_auth

Same as deposit_for_burn(), but intended to be called with an Auth struct from a dependent package. The calling package will be the "owner" (e.g. message_sender) of the message and have the ability to call replace_deposit_for_burn_with_package_auth() to update the mint recipient or destination caller. This would be similar to a wrapper contract on EVM chains calling into TokenMessenger and being the message sender. Direct callers (where EOAs are trusted and should be the owner) should use deposit_for_burn() instead.

Remarks:

  • This function uses a DepositForBurnTicket struct for parameters so that the calling package can call create_deposit_for_burn_ticket() (not version-gated) from their package with parameters, and call deposit_for_burn_with_package_auth() (version-gated) from a PTB so packages don't have to be updated during CCTP package upgrades.
  • DepositForBurnTicket also requires an Auth parameter. This is required to securely assign a sender address associated with the calling contract to the message. Any struct that implements the drop trait can be used as an authenticator, but it is recommended to use a dedicated Auth struct. Calling contracts should be careful to not expose these structs to the public or else messages from their package could be replaced. An example can be found in TokenMessengerMinter on GitHub.
  • The returned structs - BurnMessage and Message both have the copy ability. There is also no guarantee of execution ordering, so your package could create 5 DepositForBurnTickets in one transaction and they could be executed in any order depending on the PTB. Integrating packages should account for both of these scenarios.

Parameters

FieldTypeDescription
deposit_for_burn_ticketDepositForBurnTicket<T, Auth>Struct containing parameters and authenticator struct.
state&StateShared State object for the TokenMessengerMinter package.
message_transmitter_state&mut MessageTransmitterStateShared State object for the MessageTransmitter package.
deny_list&DenyListDenyList shared object for the stablecoin token T. Constant address: 0x403
treasury&mut Treasury<T>Treasury shared object for the stablecoin token T.
ctx&TxContextTxContext for the transaction.

deposit_for_burn_with_caller

Same as deposit_for_burn but with an additional parameter, destination_caller. This parameter specifies which address has permission to call receive_message on the destination domain for the message.

Remarks:

  • Intended to be called directly by EOA (rather than a dependent package). The initiating EOA will be the "owner" (e.g. message sender) of the message and have the ability to call replace_deposit_for_burn() to update the mint_recipient or destination_caller. If the calling EOA is not trusted by the mint recipient or destination caller, deposit_for_burn_with_caller_with_package_auth() should be called instead with the integrating package owning the message.

Parameters

FieldTypeDescription
coinsCoin<T>Coin of type T to be burned. Full amount in coins will be burned.
destination_domainu32Destination domain identifier.
mint_recipientaddressAddress of mint recipient on destination domain Note: If destination is a non-Move chain, mint_recipient address should be converted to hex and passed in using the @0x123 address format.
destination_calleraddressAddress of caller on destination chain.
state&StateShared State object for the TokenMessengerMinter package.
message_transmitter_state&mut MessageTransmitterStateShared State object for the MessageTransmitter package.
deny_list&DenyListDenyList shared object for the stablecoin token T. Constant address: 0x403
treasury&mut Treasury<T>Treasury shared object for the stablecoin token T.
ctx&TxContextTxContext for the transaction.

deposit_for_burn_with_caller_with_package_auth

The same as deposit_for_burn_with_caller(), but intended to be called with an Auth struct from a dependent package. The calling package will be the "owner" (e.g. message_sender) of the message and have the ability to call replace_deposit_for_burn_with_package_auth() to update the mint_recipient or destination_caller. This would be similar to a wrapper contract on EVM chains calling into TokenMessenger and being the message sender. Direct callers (where EOAs are trusted and should be the owner) should use deposit_for_burn_with_caller() instead.

Remarks:

  • This function uses a DepositForBurnWithCallerTicket struct for parameters so that the calling package can call create_deposit_for_burn_with_caller_ticket() (not version-gated) from their package, and call deposit_for_burn_with_caller_with_package_auth() (version-gated) from a PTB so dependent packages don't have to be updated during upgrades.
  • DepositForBurnWithCallerTicket also requires an Auth parameter. This is required to securely assign a sender address associated with the calling contract to the message. Any struct that implements the drop trait can be used as an authenticator, but it is recommended to use a dedicated struct. Calling contracts should be careful to not expose these structs to the public or else messages from their package could be replaced. An example can be found in TokenMessengerMinter on GitHub.

Parameters

FieldTypeDescription
deposit_for_burn_with_caller_ticketDepositForBurnWithCallerTicket<T, Auth>Struct containing parameters and authenticator struct.
state&StateShared State object for the TokenMessengerMinter package.
message_transmitter_state&mut MessageTransmitterStateShared State object for the MessageTransmitter package.
deny_list&DenyListDenyList shared object for the stablecoin token T. Constant address: 0x403
treasury&mut Treasury<T>Treasury shared object for the stablecoin token T.
ctx&TxContextTxContext for the transaction.

replace_deposit_for_burn

Replace a BurnMessage to change the mint recipient and/or destination caller. Allows the sender of a previous BurnMessage (created by deposit_for_burn or deposit_for_burn_with_caller) to send a new BurnMessage to replace the original.

Remarks:

  • The new BurnMessage will reuse the amount and burn token of the original, without requiring a new Coin<T> deposit.
  • The resulting mint will supersede the original mint, as long as the original mint has not confirmed yet onchain.
  • A valid attestation is required before calling this function.
  • This is useful in situations where the user specified an incorrect address and has no way to safely mint the previously burned USDC.

Parameters

FieldTypeDescription
original_messagevector<u8>Original message bytes (to replace).
original_attestationvector<u8>Original attestation bytes.
new_destination_callerOption<address>The new destination caller, which may be the same as the original destination caller, a new destination caller, or an empty destination caller, indicating that any destination caller is valid.
new_mint_recipientOption<address>The new mint recipient, which may be the same as the original mint recipient, or different.
state&StateShared State object for the TokenMessengerMinter package.
message_transmitter_state&mut MessageTransmitterStateShared State object for the MessageTransmitter package.
ctx&TxContextTxContext for the transaction.

replace_deposit_for_burn_with_package_auth

Same as replace_deposit_for_burn(), but intended to be called when deposit_for_burn_with_package_auth() or deposit_for_burn_with_caller_with_package_auth() was called for the original message where the calling package is the message sender.

Remarks:

  • This function uses a ReplaceDepositForBurnTicket struct for parameters so that the calling package can call create_replace_deposit_for_burn_ticket() (not version-gated) from their package with parameters, and call deposit_for_burn_with_package_auth() (version-gated) from a PTB so packages don't have to be updated during CCTP package upgrades.

Parameters

FieldTypeDescription
replace_deposit_for_burn_ticketReplaceDepositForBurnTicket<Auth>Struct containing parameters and authenticator struct.
state&StateShared State object for the TokenMessengerMinter package.
message_transmitter_state&mut MessageTransmitterStateShared State object for the MessageTransmitter package.
ctx&TxContextTxContext for the transaction.

handle_receive_message

Handles an incoming message from MessageTransmitter, and mints USDC to the recipient for valid messages. This function can only be called with a mutable reference to a Receipt object, which can only be created via a call with a valid message to the message_transmitter::receive_message() function.

state, mt_state, deny_list, and treasury parameters are all shared objects.

Remarks:

  • Returns a StampReceiptTicketWithBurnMessage struct that can be deconstructed in a dependent package (or in a PTB) via deconstruct_stamp_receipt_ticket_with_burn_message(). This struct is returned so that dependent packages can associate the BurnMessage and StampReceiptTicket together from a PTB call and guarantee that stamp_receipt() was called.
  • This must be called in a single PTB after calling receive_message() and before calling complete_receive_message(). See the Examples page for the entire flow of receiving a message.

Parameters

FieldTypeDescription
receiptReceiptOriginal message bytes (to replace).
state&StateShared State object for the TokenMessengerMinter package.
deny_list&DenyListDenyList shared object for the stablecoin token T. Constant address: 0x403
treasury&mut Treasury<T>Treasury shared object for the stablecoin token T.
ctx&TxContextTxContext for the transaction.

receive_message

Receives a message emitted from a source chain. Messages with a given nonce can only be received once for a (sourceDomain, destinationDomain) pair.

Remarks:

  • This function returns a Receipt Hot Potato struct after validating the attestation and marking the nonce as used.
  • In order to destroy the Receipt and complete the message, in a single PTB, stamp_receipt() must be called with the Receipt and an Auth struct (see message_transmitter_authenticator for an example of this), and then complete_receive_message() must be called with the StampedReceipt to emit the MessageReceived event and complete the message.
  • The receipt/stamp pattern is used to enforce atomicity and ensure the intended receiver contract is called.
  • Intended to be called directly from an EOA when a package destination_caller is not specified on the message. Please use receive_message_with_package_auth() if a package destination_caller is specified.

Parameters

FieldTypeDescription
messagevector<u8>Message bytes.
attestationvector<u8>Signed attestation of message.
state&mut StateShared State object for the TokenMessengerMinter package.
ctx&TxContextTxContext for the transaction.

receive_message_with_package_auth

Same as receive_message(), except intended to be used by a dependent package when a package is specified as destination_caller (rather than an EOA).

Remarks:

  • This function is version-gated and should be called from a PTB to prevent breaking changes when an upgrade occurs.
  • This function uses a ReceiveMessageTicket for parameters so that the calling package can call create_receive_message_ticket() (not version-gated) from their package with parameters, and call receive_message_with_package_auth() (version-gated) from a PTB so packages don't have to be upgraded during CCTP package upgrades.
  • ReceiveMessageTicket also requires an Auth parameter. This is required whenever a package is assigned as a destination_caller. destination_caller address should be set to the Auth identifier returned from the auth_caller_identifier() function with the package's Auth struct. Any struct that implements the drop trait can be used as an authenticator, but it is recommended to use a dedicated Auth struct. Calling contracts should be careful to not expose these structs to the public or else messages intended for their package could be received by others. An example can be found in TokenMessengerMinter on GitHub.

Parameters

FieldTypeDescription
receive_message_ticketReceiveMessageTicket<Auth>A Ticket struct containing the message, attestation, and an authenticator struct.
state&mut StateShared State object for the TokenMessengerMinter package.
ctx&TxContextTxContext for the transaction.

stamp_receipt

Stamps a Receipt struct after verifying the intended package acknowledged the message (through the Auth struct) by returning a StampedReceipt struct that can be used to complete the message via complete_receive_message().

Remarks:

  • This function is version-gated and should be called from a PTB to prevent breaking changes in dependent packages when a CCTP upgrade occurs.
  • create_stamp_receipt_ticket() is safe to be called directly from a package (not version-gated), and it's returned Ticket struct can be passed into stamp_receipt() in a PTB.

Parameters

FieldTypeDescription
stamp_receipt_ticketStampReceiptTicket<Auth>Ticket struct created by create_stamp_receipt_ticket() with the Receipt and Auth struct.
state&mut StateShared State object for the TokenMessengerMinter package.
ctx&TxContextTxContext for the transaction.

complete_receive_message

Completes the message by emitting a MessageReceived event for a stamped receipt and destroying the receipt. Cannot be called without a StampedReceipt (returned from stamp_receipt()).

Parameters

FieldTypeDescription
stamped_receiptStampedReceiptA stamped receipt returned from a stamp_receipt() call.
state&StateShared State object for the TokenMessengerMinter package.

send_message

Sends a message to the destination domain and recipient. The created Message struct is returned, but it is not required to do anything with this struct, it is returned for convenience.

Remarks:

  • This function uses a SendMessageTicket for parameters so that the calling package can call create_send_message_ticket() (not version-gated) from their package with parameters, and call send_message() (version-gated) from a PTB so packages don't have to be updated during CCTP package upgrades.
  • For USDC transfers, this function is called directly by the TokenMessengerMinter package in deposit_for_burn().
  • SendMessageTicket also requires an Auth parameter. This is required in order to assign a sender to the message. Any struct that implements the drop trait can be used as an authenticator, but it is recommended to use a dedicated Auth struct. Calling contracts should be careful to not expose these objects to the public or else their messages could be replaced. An example can be found in TokenMessengerMinter on GitHub.
  • The returned struct (Message) has the copy ability. There is also no guarantee of execution ordering, so your package could create 5 SendMessageTickets in one transaction and they could be executed in any order depending on the PTB. Integrating packages should account for both of these scenarios.

Parameters

FieldTypeDescription
send_message_ticketSendMessageTicket<Auth>A struct containing the necessary information to send a message created via create_send_message_ticket().
state&mut StateShared State object for the TokenMessengerMinter package.

send_message_with_caller

Same as send_message() but with an additional parameter, destination_caller. This parameter specifies which address has permission to call receive_message() on the destination domain for the message.

Parameters

FieldTypeDescription
send_message_with_caller_ticketSendMessageWithCallerTicket<Auth>A struct containing the necessary information to send a message created via create_send_message_with_caller_ticket().
state&mut StateShared State object for the TokenMessengerMinter package.

replace_message

Replace a message with a new message body and/or destination caller. The original_attestation must be a valid attestation of original_message, produced by Circle's attestation service.

Remarks:

  • The sender package of the replaced message must be the same as the caller of the original message. This is identified using the Auth generic parameter. See stamp_receipt for more info on Auth structs.

Parameters

FieldTypeDescription
replace_message_ticketReplaceMessageTicket<Auth>A struct containing the necessary information to send a message created via create_replace_message_ticket().
state&mut StateShared State object for the TokenMessengerMinter package.

Destination caller is a message field that specifies which address has permission to call receive_message() on the destination domain for the given message. On Sui this can either be an EOA (use receive_message()) or an Auth struct address for a package (use receive_message_with_package_auth()). Using a package destination caller allows integrators to run any atomic action in the same transaction that the message is received in.

In order to determine the address to use for the destination caller field for Sui destination messages, please call message_transmitter::auth::auth_caller_identifier() with your Auth struct type.

Outgoing mint recipient addresses from Sui are passed as Sui address types and can be treated the same as a bytes32 mint recipient parameter on EVM implementations.

Sui mint recipient addresses from other chains should be treated the same as a hex bytes32 parameter.

CCTP packages on Sui are upgradable. Public functions like deposit_for_burn(), receive_message(), etc. are version-gated. This means if the CCTP packages are upgraded, the old versions of these functions will no longer be callable. Because of this, we do not recommend calling these functions directly from packages, and instead recommend calling the create ticket functions (not version-gated) directly from dependent packages, returning the created Ticket from your package, and then calling the main public function (e.g. deposit_for_burn() or receive_message()) from a PTB. By using the create ticket functions, dependent packages can securely set the parameters and Auth struct for the function call from within the package, and only have to update PTBs when CCTP packages are upgraded.

Integrating with the CCTP Sui packages from other packages is different from non-Sui implementations. Rather than directly wrapping the CCTP-Sui packages like one would in Solidity, on Sui packages should interact with CCTP packages in a more composable way. Third party packages should follow the Ticket pattern with a dedicated and private Auth struct as described below.

Auth structs are used throughout the CCTP packages in functions intended to be called from other dependent packages. The auth_caller_identifier() function is used to uniquely identify other packages by hashing the full object type of the type passed in. Any struct that implements the drop trait can be used as an authenticator, but it is recommended to use a dedicated auth struct. Calling contracts should be careful to not expose these structs to the public or else messages from their package could be forged. An example can be found in TokenMessengerMinter on GitHub.

The Ticket pattern is a pattern used in CCTP-Sui that enables the composability of CCTP with third-party packages. The pattern enables a third-party integrator (package) to create a Ticket ("hot potato") for a designated operation directly in their package without having to upgrade their packages with future CCTP upgrades. Only PTBs would need to be updated.

Ticket structs contain parameters for specific interactions with CCTP packages. They can only be created from and consumed by the CCTP packages in a specific interaction, and do not have drop or store abilities, so must be used in the PTB where they are created. They also contain an Auth field that should only be created by the third-party package. The calling PTB should handle the Ticket by calling the relevant CCTP package, which will recognize the third-party integrator as the action initiator.

The following public functions (designed for third-party integrators, EOAs should use the entry versions) are implemented following the Ticket pattern. Each of them creates or consumes their own specific Ticket type:

message_transmitter:

  • receive_message_with_package_auth()
  • stamp_receipt()

token_messenger_minter:

  • deposit_for_burn_with_package_auth()
  • deposit_for_burn_with_caller_with_package_auth()
  • replace_deposit_for_burn_with_package_auth()

For example, a typical workflow in a PTB to replace a deposit by an integrator would be:

  1. The integrating package calls create_replace_deposit_for_burn_ticket() with an Auth struct it defined, and returns this ticket.
  2. The PTB calls deposit_for_burn_with_caller_with_package_auth() with the ticket on behalf of the integrator.
  3. token_messenger_minter will validate if the type hash of Auth matches the original sender in the burn message.

Due to the composability of Sui and PTBs, along with the Ticket pattern, there is no guarantee of ordering of calls within PTBs. The Ticket pattern introduces behaviors similar to asynchronous functions in ordinary programming contexts: when an integrator creates a ticket and returns it to the PTB, it is signaling an intention to execute the logic function, and the properties of the Move type system carry the guarantee that the function will indeed be eventually executed before the end of the transaction. However, no guarantee is given regarding the relative order of execution: the PTB is free to consume the tickets in any order it sees fit. While this has no security implications on the internal coherence of CCTP itself, integrators should carefully evaluate whether their own logic is somehow dependent on a specific order of execution of the CCTP functions.

For example, a PTB could create 5 DepositForBurnTicket structs and execute them in any order. Similarly on the Sui destination side, 5 messages could be received in MessageTransmitter, and then received (and thus the USDC minted) in TokenMessengerMinter in a completely different order. If any pre or post actions are taken in third party packages, these could also come in an unexpected ordering, so this scenario should be handled accordingly in third party packages.

An example of this with receiving deposit_for_burn() messages on Sui can be seen below.

This example assumes the destination_caller for the message is set to the auth address for your package's Auth struct.

JavaScript
// Prepare the ReceiveMessageTicket by calling create_receive_message_ticket() from within your package.
let receive_msg_ticket = your_package::prepare_receive_message_ticket(message, attestation);
// Receive the message on MessageTransmitter.
let receipt = message_transmitter::receive_message_with_package_auth(receive_msg_ticket, ...);
// Pass the Receipt into TokenMessengerMinter to mint the USDC.
let ticket_with_burn_message = token_messenger_minter::handle_receive_message(receipt, ...);
// In your package you can call deconstruct_stamp_receipt_ticket_with_burn_message to deconstruct the ticket
// and burn_message and securely take some action with the burn_message (e.g. swap some tokens, send them somewhere, etc.)
let stamp_receipt_ticket = your_package::take_some_action(ticket_with_burn_message, ...);
// Stamp the receipt
let stamped_receipt = message_transmitter::stamp_receipt(stamp_receipt_ticket);
// Complete the message and destroy the StampedReceipt
message_transmitter::complete_receive_message(stamped_receipt);

A similar example can be seen on the deposit_for_burn() side:

JavaScript
// Prepare the DepositForBurnWithCallerTicket by calling create_deposit_for_burn_with_caller_with_package_auth
// directly from your package with the input parameters and your Auth struct. Integrators can also take other
// actions here as needed.
let deposit_for_burn_ticket = your_package::prepare_deposit_for_burn_ticket(coins, ...);
// Call deposit for burn and burn the USDC
let (burn_message, message) = token_messenger_minter::deposit_for_burn_with_caller_with_package_auth(
  deposit_for_burn_ticket,
  ...
);
// Optionally, take some other action in your package based on the output message.
// Note that BurnMessage and Message have the copy ability so the possibility of them being copied should be
// handled in third party packages if post-actions are needed.
your_package::post_deposit_for_burn(burn_message, message, ...);
Did this page help you?
© 2023-2025 Circle Technology Services, LLC. All rights reserved.