To use the Wormhole message passing protocol to enable trustless cross-chain token sales.
## Background
Token sales are one of the major applications of today's blockchains.
Currently they are either conducted on a single chain in a trustless fashion or in a centralized fashion with support to contribute tokens from multiple chains.
Using wormhole we can bridge this gap - Allow users to contribute assets on all supported chains and issue a token that we can bridge to all chains for them to claim after the sale has been concluded.
## Goals
We want to implement a generalized, trustless cross-chain mechanism for token sales.
- Automatically relay messages across chains. The design assumes there is always a party interested in synchronizing the data across chains, let it be the token issuer or an investor who wants to claim its tokens.
These contracts are upgradable with a single key (see **Owner Only** section of each program for relevant methods). We encourage the implementor to use a multisig wallet for deployment at the very least.
The `createSale()` method deposits the offered tokens, assigns an ID which identifies the sale and attests a `SaleInit` packet over the wormhole. This packet contains all the information from above. It will also attest a `SolanaSaleInit` packet over the wormhole if any Solana tokens are accepted as collateral in the sale.
The attested `SaleInit` packet (or the `SolanaSaleInit`) is submitted to the `TokenSaleContributor` contracts. The `TokenSaleContributor` contract stores the sale information locally which is relevant to its chain.
The `TokenSaleConductor` contract can terminate the sale by calling `abortSaleBeforeStartTime()` before the sale period begins. Only the wallet that called `createSale()` can invoke this method.
During the start and end timestamp the `TokenSaleContributor` contracts accept contributions in the specified tokens. The `contribute()` method takes an argument `bytes memory sig` which is a third party signature stating that KYC was performed for a particular contribution. The `TokenSaleContributor` calls `verifySignature` to recover a public key from the passed signature. If the public key matches the `authority` address in the `TokenSaleContributor` state, the contribution is permitted. In the case where the KYC `authority` key is compromised, the contract deployer can invoke the `updateSaleAuthority()` method on the `TokenSaleConductor`. An `AuthorityUpdated` packet will be attested over the wormhole, and the `TokensSaleContributor` contracts will update the known `authority` for the specified sale by calling `saleAuthorityUpdated()`.
After the sale duration, anyone can call the `attestContributions()` method on the `TokenSaleContributor`, which attests a `ContributionsSealed` packet over the wormhole. If the sale accepts Solana tokens, the Solana `TokenSaleContributor` will send the `solanaTokenAccount` in the `ContributionsSealed` packet. This tells the `TokenSaleConductor` which account to send the sale tokens to on Solana after the sale is completed.
After all contributions have been collected, anyone can call the `sealSale()` method on the `TokenSaleConductor`.
The method evaluates whether the minimum raise amount has been met using the conversion rates specified initially (a later version could use rates from an oracle at closing). The conversion rates are scaled based on the accepted token decimals on the `TokenSaleConductor` chain relative to the token decimals on the native chain. It is crucial that the conversion rates are scaled properly in order to correctly calculate token allocations. In case it was successful it:
- Calculates allocations and excess contributions (if total contributions sum to a value larger than the maximum raise amount)
- Excess contributions are calculated by taking the difference between the maximum raise amount and the total contributions.
Each contributor receives excess contributions proportional to their contribution amount (individualContribution / totalContributions \* totalExcessContributions)
- Emits another `SaleSealed` packet if the sale accepts Solana tokens as collateral. The message is in the same format as the original `SaleSealed` packet, but only contains information regarding Solana token allocations. This is necessary due to VAA size contraints on Solana.