- Hook
- Adds smart contract functionality to the XRP Ledger: layer one custom code to influence the behavior and flow of transactions.
- Hooks are small, efficient pieces of code being defined on an XRPL account, allowing logic to be executed before and/or after XRPL transactions.
- Hook Account
- Refers to the XRPL account that has the Hook installed on.
- This account executes the Hook smart contract logic when triggered by a transaction.
- It’s code can’t automatically be invoked on its own, it must be triggered by an incoming transaction.
- It’s able to store application data to its Hook State.
- Hook State
- Refers to the long-term application state that the Hook Account reads from and writes to.
- The state is saved to the Hook Account and can be queried using RPC commands (
account_info
&account_namespace
) - The state is represented as a key-value data structure where each entry’s key is 32 bytes and value is 256 bytes.
- The value bytes is subject to change based on validator voting. At time of writing, the value is set to a max of 256 bytes.
- Invoke Transaction Type
- It’s a new transaction type introduced in Hooks.
- Useful for triggering a Hook account to execute its smart contract logic without having to send XRP.
- Remember: Hook code can’t automatically be executed, they must be triggered by a transaction. Invoke transaction is meant for doing this.
- Hook Execution Count
- Refers to the number of operations a Hook code can execute worst-case.
- Each Hook can only execute a maximum of 65,535 operations.
- Owner Reserve Fees
- 1 Owner Reserve Fee for each Hook State entry
- Hook Execution Count (total worst-case)
- Maximum possible execution instructions are 65,535
- If it exceeds, then split the logic into multiple Hooks
- Generally speaking, you do one operation per hook
- If you really have a very big hook that needs lots of operations make more accounts and use grants to access a shared state
- 1 KB (1000 bytes) Memo payload limit
- Invoke blob payload fee is 1 drop per byte
- Max of 4 Hooks installed on a single account
- If more than 4 Hooks are required, then
- Hook State entry key is 32 bytes
- Hook State entry value is 256 bytes (subject to changed based on validator voting)
- A Hook can’t automatically send payments/transactions (refund/reward) on its own; this is true for most crypto smart contracts. The Hook must be triggered by another transaction.
- Invoke is the new transaction type introduced in Hooks where it serves as a trigger transaction that doesn’t require Amount to be sent.
- FYI: without Invoke, AccountSet would be the trigger transaction to use. It’s currently used for Escrow payments
- Campaign
- refers to a fundraising effort on a crowdfunding platform where an individual or organization seeks to raise money from a large number of people for a specific project, idea, or cause.
- Milestone
- refers to a specific, measurable, and time-bound achievement or objective that a campaign creator sets in order to reach their fundraising goal.
- Milestone Payout
- TODO
- Backer
- refers to an individual who supports a campaign by making a financial contribution towards its goal.
- A crowdfunding project is created by creating a campaign.
- A campaign must contain the following mandatory information:
- Owner
- The owner of the campaign; represented as an XRPL account address.
- The Sender account in a transaction to create a new campaign will be assigned as the owner of the campaign.
- Title
- The campaign title.
- 75 character limit
- Description
- The campaign description.
- 2,500 character limit
- Overview URL
- A URL endpoint where it contains media assets (photos/videos/etc) and additional details about the campaign.
- 2,300 character limit
- Fund Raise Goal
- Represented as XRP drops
- Fund Raise End Date
- Represented as a unix timestamp
- Milestone(s)
- Title
- The milestone title
- 75 character limit
- Payout percentage
- The payout percentage of a single milestone.
- This is a percentage of the remaining funds in a campaign that are paid out to the campaign owner for successfully meeting a milestone.
- Milestone End Date
- The end date of a milestone for which a milestone payout can occur if at least half of backers approve it.
- Represented as a unix timestamp
- Title
- Owner
- A campaign’s details cannot be modified/updated after creating it.
- Keeping it immutable prevents possible scam exploits.
- A campaign must contain the following mandatory information:
- A campaign goes through 2 phases (Fund Raise & Milestone payouts):
- Fund Raise phase
- Raise funds to meet a campaign’s fund goal. This is done by backers funding a campaign.
- The fund raise goal must be met by the fund raise end date.
- If it’s not, then the campaign is cancelled.
- Otherwise, the campaign can proceed to its next phase of (milestone payouts).
- Milestone Payouts phase
- Backers whom funded a campaign will have the ability to vote on each milestone:
- Vote to approve the milestone payout
- By default, a backer who funds a campaign has his/her vote set to approve the current milestone payout.
- By the milestone end date, if there’s at least 50% of backers who approve the milestone, then the milestone payout will be sent to the campaign owner.
- The payout is a percentage of the remaining funds.
- Vote to reject a milestone to get a refund and cancel the campaign.
- If at one time, a majority vote (51% of backers) reject a milestone, then the campaign will issue a refund to all backers and cancel the campaign.
- Refunds will take the remaining the funds in a campaign and distribute it evenly to across all backers.
- If at one time, a majority vote (51% of backers) reject a milestone, then the campaign will issue a refund to all backers and cancel the campaign.
- Vote to approve the milestone payout
- After the final milestone payout is approved, the campaign owner will have received all funds and the campaign successfully ends.
- Backers whom funded a campaign will have the ability to vote on each milestone:
- Fund Raise phase
- Create Campaign
- View Campaigns
- Fund Campaign
- Vote Reject
- Vote Approve
- Request Refund Payment
- Request Milestone Payout Payment
- These are the application models using JavaScript types.
- Campaign
**destinationTag**
-number
state
-number
- Possible states: TODO
**owner**
-string
- XRP address
**title**
-string
- Max length 75 uft-8 characters
**description**
-string
- Max length 2,500 uft-8 characters
**overviewUrl**
-string
- Max length 2,300 uft-8 characters
**fundRaiseGoalInDrops**
-BigInt
- We use
BigInt
because the maximum bit integer supported in JavaScript is 53 bits and XRP drops is a 64 bit unsigned integer. Therefore, to work with integers larger than 53 bits, we use the native JS typeBigInt
- We use
**fundRaiseEndDateInUnixSeconds**
-number
**totalAmountRaisedInDrops**
-BigInt
**totalAmountRewardedInDrops**
-BigInt
**totalReserveAmountInDrops**
-BigInt
**milestones**
-Milestone[]
**fundTransactions**
-FundTransaction[]
**backers**
-Backer[]
- Milestone
state
-number
- Possible states: TODO
**endDateInUnixSeconds**
-number
**payoutPercent**
-number
**rejectVotes**
-number
**title**
-string
- Max length 75 uft-8 characters
- FundTransaction
**id**
-number
**account**
-string
- XRP address
**state**
-number
- Possible states: TODO
**amountInDrops**
-BigInt
- Backer
**account**
-string
- XRP address
**fundTransactions**
-FundTransaction[]
**uint8**
- 1-byte unsigned integer**uint32**
- 4-byte unsigned integer**uint64**
- 8-byte unsigned integeruint224
- 28-byte unsigned integer**varString**
- variable-byte string (with its length prefixed)stringLengthPrefix
- variable-byte unsigned integer- Indicates the string length. This prefix is the first bytes of the
**varString**
and its bytes are of variable size. - For a general case,
stringLengthPrefix
bytes is 1 byte which can represent a length of up to 256 characters.
- Indicates the string length. This prefix is the first bytes of the
value
- fixed-byte string (size fixed to max length instring-length-prefix
)- Contains the actual string content.
- The size is fixed to the maximum characters that can be represented in
stringLengthPrefix
- If the actual string content is less than the maximum characters, then trailing zeros are added to fill up the remaining space.
- This design allows for easy traversal of strings inside a Hook C script.
**xrpAddress**
- 36-byte unsigned integer (with its length prefixed)xrpAddressLengthPrefix
- 1-byte unsigned integer- Indicates the XRP Address length (varies between 25-35 characters). This prefix is the first byte of the
**xrpAddress**
and its bytes are of variable size.
- Indicates the XRP Address length (varies between 25-35 characters). This prefix is the first byte of the
value
- 35-byte string (size is always fixed to max length of 35 characters)- Contains the actual XRP Address content.
- The size is fixed to the maximum length (35 characters) of an XRP address
- Similar to
varString
, if the actual XRP Address content is less than the maximum characters, then trailing zeros are added to fill up the remaining space. - This design allows for easy lookup/traversal of data inside a Hook C script.
- Similar to
**model**
- variable-byte object (contains various data types)- A model can be thought of as a container that can hold different types of data, including other models, in a structured way. This allows for complex data structures to be built up from simpler components, and for data to be organized and accessed in a logical and efficient manner. The specific types and structures included in a model will depend on the specific requirements of the application or system being developed.
**varModelArray**
- variable-byte model array (with its length prefixed)model-length-prefix
- 1-byte unsigned integer- The first byte of
varModelArray
that indicates the length of the model array. This prefix is the first byte of thevarModelArray
and is fixed to 1 byte. - It is fixed to 1 byte because there is no use case for having more than 256 elements in a model array.
- The first byte of
value
- variable-byte model array- Contains the actual model array.
- Unlike
varString
, the size isn’t fixed to the maximum models that can be represented inmodel-length-prefix
- Its easy lookup/traversal is already derived from the impositions of previous data types.
-
Each Hook State entry is comprised of a key-value pair
- Key is 32 bytes
- Value is 256 bytes (based on validator voting)
-
Hook State Key
- Key - 32 bytes
- data lookup flag - 28 bytes
- destination tag - 4 bytes
- Key - 32 bytes
-
Hook State Value
- Value - 256 bytes
- Since value is limited to 256 bytes, it can contain 1 model, fragmented model, or even multiple models (paginated) in a single entry.
- 1 model:
- General Info
- Fragmented models:
- Description - 1/10 data instance occupies a single entry
- Overview URL - 1/10 data instance occupies a single entry
- Paginated models:
- Milestones - 2/1 data instances occupies a single entry
- FundTransactions - 5/1 data instances occupies a single entry
- 1 model:
- Refer to Hook State Visualization table for clarification.
- Since value is limited to 256 bytes, it can contain 1 model, fragmented model, or even multiple models (paginated) in a single entry.
- Value - 256 bytes
-
Hook State Visualization Table
- A transaction mode flag represents what application operation to execute in the Hook Account (the Hook C script).
- This flag is always represented as a 1-byte unsigned hexadecimal integer.
**MODE_CREATE_CAMPAIGN_PART_A_FLAG**
-0x00
**MODE_CREATE_CAMPAIGN_PART_B_FLAG**
-0x01
**MODE_FUND_CAMPAIGN_FLAG**
-0x02
**MODE_VOTE_REJECT_FLAG**
-0x03
**MODE_VOTE_APPROVE_FLAG**
-0x04
**MODE_REQUEST_REFUND_PAYMENT_FLAG**
-0x05
**MODE_REQUEST_MILESTONE_PAYOUT_PAYMENT_FLAG**
-0x06
CreateCampaignPayloadPartA
-**model**
(194 bytes)modeFlag
-**MODE_CREATE_CAMPAIGN_PART_A_FLAG
** (1 byte)title
-varString
(76 bytes)- stringLengthPrefix (1 byte)
- value (75 bytes)
fundRaiseGoalInDrops
-**uint64**
(8 bytes)fundRaiseEndDateInUnixSeconds
-**uint32**
(4 bytes)totalAmountRaisedInDrops
-**uint64**
(8 bytes)totalAmountRewardedInDrops
-**uint64**
(8 bytes)totalReserveAmountInDrops
-**uint64**
(8 bytes)totalMilestones
-**uint8**
(1 byte)
CreateCampaignPayloadPartB
-**model**
(5,666 bytes)modeFlag
-**MODE_CREATE_CAMPAIGN_PART_B_FLAG
** (1 byte)description
-varString
(2502 bytes)- stringLengthPrefix (2 bytes)
- value (2500 bytes)
overviewURL
-varString
(2302 bytes)- stringLengthPrefix (2 bytes)
- value (2300 bytes)
milestones
-varModelArray
(max 861 bytes)model-length-prefix
-**uint8**
(1 byte)value
-**model(s)**
(max 860 bytes)milestoneModel
- (86 bytes)state
-uint8
(1 byte)endDateInUnixSeconds
-**uint32**
(4 bytes)payoutPercent
-uint8
(1 byte)rejectVotes
-**uint32**
(4 bytes)title
-varString
(76 bytes)- stringLengthPrefix (1 byte)
- value (75 bytes)
**FundCampaignPayload**
-**model
** (1 byte)modeFlag
-**MODE_FUND_CAMPAIGN_FLAG
** (1 byte)
**VoteRejectPayload**
-**model
** (1 byte)modeFlag
-**MODE_VOTE_REJECT_FLAG
** (1 byte)fundTransactionId
-**uint32**
(4 bytes)
**VoteApprovePayload**
-**model
** (1 byte)modeFlag
-**MODE_VOTE_APPROVE_FLAG
** (1 byte)fundTransactionId
-**uint32**
(4 bytes)
RequestRefundPaymentPayload
-**model
** (1 byte)modeFlag
-**MODE_REQUEST_REFUND_PAYMENT_FLAG
** (1 byte)fundTransactionId
-**uint32**
(4 bytes)
RequestMilestonePayoutPaymentPayload
-**model
** (1 byte)modeFlag
-**MODE_REQUEST_MILESTONE_PAYOUT_PAYMENT_FLAG
** (1 byte)
- An application state flag represents the current state of an entity: Campaign, Milestone, FundTransaction.
- This flag is always represented as a 1-byte unsigned hexadecimal integer.
- Campaign states:
state
-uint8
(1 byte) - possible values:**CAMPAIGN_STATE_FUND_RAISE_FLAG**
-0x00
**CAMPAIGN_STATE_MILESTONE_1_FLAG**
-0x01
**CAMPAIGN_STATE_MILESTONE_2_FLAG**
-0x02
**CAMPAIGN_STATE_MILESTONE_3_FLAG**
-0x03
**CAMPAIGN_STATE_MILESTONE_4_FLAG**
-0x04
**CAMPAIGN_STATE_MILESTONE_5_FLAG**
-0x05
**CAMPAIGN_STATE_MILESTONE_6_FLAG**
-0x06
**CAMPAIGN_STATE_MILESTONE_7_FLAG**
-0x07
**CAMPAIGN_STATE_MILESTONE_8_FLAG**
-0x08
**CAMPAIGN_STATE_MILESTONE_9_FLAG**
-0x09
**CAMPAIGN_STATE_MILESTONE_10_FLAG**
-0x0A
**CAMPAIGN_STATE_FAILED_FUND_RAISE_FLAG**
-0x0B
**CAMPAIGN_STATE_FAILED_MILESTONE_FLAG**
-0x0C
**CAMPAIGN_STATE_COMPLETED_FLAG**
-0x0D
- Milestone states:
state
-uint8
(1 byte) - possible values:**MILESTONE_STATE_UNSTARTED_FLAG**
-0x00
**MILESTONE_STATE_IN_PROGRESS_FLAG**
-0x01
**MILESTONE_STATE_FAILED_FLAG**
-0x02
**MILESTONE_STATE_COMPLETED_FLAG**
-0x03
**MILESTONE_STATE_PAID_FLAG**
-0x04
- FundTransaction states:
state
-uint8
(1 byte) - possible values:**FUND_TRANSACTION_STATE_REJECT_FLAG**
-0x00
**FUND_TRANSACTION_STATE_APPROVE_FLAG**
-0x01
**FUND_TRANSACTION_STATE_REFUNDED_FLAG**
-0x02
- A hook state key data lookup flag represents which campaign data to lookup from the Hook Account’s hook state.
- This flag is always represented as a 28-byte unsigned hexadecimal integer.
**DATA_LOOKUP_GENERAL_INFO_FLAG**
-0x00
**DATA_LOOKUP_DESCRIPTION_FRAGMENT_START_INDEX_FLAG**
-0x01
**DATA_LOOKUP_DESCRIPTION_FRAGMENT_END_INDEX_FLAG**
-0x0A
**DATA_LOOKUP_OVERVIEW_URL_FRAGMENT_START_INDEX_FLAG**
-0x0B
**DATA_LOOKUP_OVERVIEW_URL_FRAGMENT_END_INDEX_FLAG**
-0x13
**DATA_LOOKUP_MILESTONES_PAGE_START_INDEX_FLAG**
-0x14
**DATA_LOOKUP_MILESTONES_PAGE_END_INDEX_FLAG**
-0x18
**DATA_LOOKUP_FUND_TRANSACTIONS_PAGE_START_INDEX_FLAG**
-0x19
**DATA_LOOKUP_FUND_TRANSACTIONS_PAGE_END_INDEX_FLAG**
-0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
HookState
-**model**
entries
-**HookStateEntry[]**
HookStateEntry
-**model**
key
-**HookStateKey**
value
-HookStateValue
**HookStateKey**
-**model**
encoded
- 32-byte stringdecoded
- 32-byte objectdataLookupFlag
-uint224
(28-byte unsigned integer)destinationTag
-uint32
(4-byte unsigned integer)
**HookStateValue**
-**model**
dataLookupFlag
-uint224
(28-byte unsigned integer)encoded
- 256-byte stringdecoded
-HSVCampaignGeneralInfoDecoded
orHSVCampaignDescriptionFragmentDecoded
orHSVCampaignOverviewURLFragmentDecoded
orHSVCampaignMilestonesPageDecoded
orHSVCampaignFundTransactionsPageDecoded
HSVCampaignGeneralInfoDecoded
state
-uint8
(1 bytes)owner
-xrpAddress
(36 bytes)- stringLengthPrefix (1 byte)
- value (35 bytes)
title
-varString
(76 bytes)- stringLengthPrefix (1 byte)
- value (75 bytes)
fundRaiseGoalInDrops
-**uint64**
(8 bytes)fundRaiseEndDateInUnixSeconds
-**uint32**
(4 bytes)totalAmountRaisedInDrops
-**uint64**
(8 bytes)totalAmountRewardedInDrops
-**uint64**
(8 bytes)totalReserveAmountInDrops
-**uint64**
(8 bytes)totalMilestones
-**uint8**
(1 bytes)totalFundTransactions
-**uint32**
(4 bytes)
HSVCampaignDescriptionDecoded
fragments
-HSVCampaignDescriptionFragmentDecoded[]
(Max 2,560 bytes)compositeValue
-string
(Max 2,560 bytes)
HSVCampaignDescriptionFragmentDecoded
value
-varString
(Max 255 bytes, discard prefix byte)
**HSVCampaignOverviewURLDecoded**
fragments
-HSVCampaignOverviewURLFragmentDecoded[]
(Max 2,560 bytes)compositeValue
-string
(Max 2,560 bytes)
HSVCampaignOverviewURLFragmentDecoded
value
-varString
(Max 255 bytes)
**HSVCampaignMilestonesDecoded**
pages
-HSVCampaignMilestonesPageDecoded[]
(Max 1,280 bytes)compositeValue
-Milestone[]
(Max 1,280 bytes)
**HSVCampaignMilestonesPageDecoded**
value
-Milestone[] - max length 2
(Max 255 bytes, discard prefix byte)
**HSVCampaignFundTransactionsDecoded**
pages
-HSVCampaignFundTransactionsPageDecoded[]
(Max bytes is virtually unlimited)compositeValue
-FundTransaction[]
(Max bytes is virtually unlimited)
**HSVCampaignFundTransactionsPageDecoded**
value
-FundTransaction[] - max length 5
(Max 255 bytes, discard prefix byte)
**ApplicationState**
campaigns
-**Campaign[]**
**HookStateToAppStateUtility**
processHookState(): **ApplicationState**
- There are 2 entities that will be communicating with each other:
- Client
- Hook
- 1. Create Campaign
- Client submits a
Payment
transaction to Hook Account with these fields:- Campaign destination tag (randomly generated on client-side).
- Hex encoded in
Memos
payload:CreateCampaignPayloadPartA
- The Hook is invoked on the incoming transaction.
- Hook parses the
Payment
transaction- Destination Tag
- Transaction Mode from
Memos
payload Amount
(create campaign deposit fee)
- Hook parses the
- Transaction mode must be
**MODE_CREATE_CAMPAIGN_PART_A_FLAG**
in order for Create Campaign Part A logic to be invoked.- Rollback transaction on unrecognized mode
- Hook will check if
Amount
has enough to cover the new campaign deposit requirement- Campaign deposit is
1,300 XRP
1,300 XRP = (26 Hook State entries required for creating a new campaign) * (50 Owner Reserve Fee XRP)
- This will be set to
totalReserveAmountInDrops
in Campaign General Info
- If it doesn’t, rollback the transaction.
- Campaign deposit is
- Hook updates its Hook State
- Add new Campaign General Info entry
- IMPORTANT: include
totalReserveAmountInDrops
(calculated from previous step)
- IMPORTANT: include
- Add new Campaign General Info entry
- Hook accepts
Payment
transaction - Client submits an
Invoke
transaction to Hook Account with these fields:- Campaign destination tag
- Hex encoded in
Blob
payload:CreateCampaignPayloadPartB
- Description
- OverviewURL
- Milestones
- Hook is invoked on the incoming transaction
- Hook parses the
Invoke
transaction- Destination Tag
- Transaction mode from
Memos
payload
- Transaction mode from
- Destination Tag
- Hook parses the
- Transaction mode must be
**MODE_CREATE_CAMPAIGN_PART_B_FLAG**
in order for Create Campaign Part B logic to be triggered.- Rollback transaction if it’s an unrecognized mode
- Hook updates its Hook State by adding remaining campaign data:
- Description
- OverviewURL
- Milestones
- Hook accepts
Invoke
transaction
- Client submits a
- 2. View Campaigns
- Client sends RPC requests to query Hook State on Hook Account
account_info
- Get hook namespace_id and use it as a param in the next RPC command
account_namespace
- Get
namespace_entries
which will contain the HookState entries
- Get
- Process the Hook State data to get Application State:
- Call
HookStateToAppStateUtility.processHookState(**namespace_entries**)**
to get**ApplicationState
instance
- Call
- View campaigns by looking at
**ApplicationState.campaigns**
- Client sends RPC requests to query Hook State on Hook Account
- 3. Fund Campaign
- Client submits a
Payment
transaction to Hook Account with these fields:- Campaign destination tag
- Hex encoded in
Memos
payload:**FundCampaignPayload**
- The Hook is invoked on the incoming transaction.
- Hook Account parses the
Payment
transaction- destination tag
- transaction mode from memo payload
- Amount
- Hook Account parses the
- Transaction mode must be
**MODE_FUND_CAMPAIGN_FLAG**
in order for Fund Campaign logic to be invoked.- Rollback transaction on unrecognized mode
- Hook will check if
Amount
has enough to cover the reserve fee for adding new data to Hook State- Fund Campaign fee is
10 XRP
10 XRP = (1/5 Hook State entry required for adding a FundTransaction entity) * (50 Owner Reserve Fee XRP)
**fundCampaignFeeInDrops** = 10,000,000 drops
- If it doesn’t, rollback the transaction.
- Fund Campaign fee is
- Hook will check if campaign state is in
**CAMPAIGN_STATE_FUND_RAISE_FLAG**
- It will read this campaign state from Hook State to determine this.
- If it’s not, rollback the transaction
- Hook updates its Hook State
- Update Campaign General Info
- increment
totalFundTransactions
- Update ``totalReserveAmountInDrops += fundCampaignFeeInDrops`
- Update
totalAmountRaisedInDrops += PaymentTransaction.Amount - **fundCampaignFeeInDrops**
- increment
- Add new FundTransaction entry
- Update Campaign General Info
- Hook accepts
Payment
transaction - Client queries Hook State to fetch and derive FundTransaction id
- FundTransaction id is used later for other backer related transactions such as voting and requesting refund payment.
- Client submits a
- 4. Vote Reject
- Client submits an
Invoke
transaction to Hook Account with these fields:- Campaign destination tag
- Hex encoded in
Blob
payload:**VoteRejectPayload**
- The Hook is invoked on the incoming transaction.
- Hook Account parses the
Invoke
transaction- Destination Tag
- From
Blob
payload:- Transaction mode
fundTransactionId
- Hook Account parses the
- Transaction mode must be
**MODE_VOTE_REJECT_FLAG**
in order for Vote Reject logic to be invoked.- Rollback transaction on unrecognized mode
- Hook will check if campaign state is in a milestone state (for ex.
CAMPAIGN_STATE_MILESTONE_1_FLAG
)- It will read this campaign state from Hook State to determine this.
- If it’s not, rollback the transaction
- Hook will check if current milestone has reached its end date
- If it has, update the Campaign General Info data in Hook State
- Update
state
to next campaign state - IMPORTANT: use this as the new current state
- Update
- If it has, update the Campaign General Info data in Hook State
- Hook will check if
fundTransactionId
exists in Hook State and if Sender Account is associated with it- It will read FundTransaction data from Hook State by using
fundTransactionId
as an index to the FundTransaction index range - If
fundTransactionId
index doesn’t have data or the Sender Account isn’t associated with it:- Rollback the transaction
- It will read FundTransaction data from Hook State by using
- Hook updates its Hook State
- Skip to step 8 if
state
is already in**FUND_TRANSACTION_STATE_REJECT_FLAG**
- Update FundTransaction data
- Change
state
to**FUND_TRANSACTION_STATE_REJECT_FLAG**
- Change
- Update Milestone data
- increment
refundVotes
- increment
- Skip to step 8 if
- Hook accepts
Invoke
transaction
- Client submits an
- 5. Vote Approve
- Client submits an
Invoke
transaction to Hook Account with these fields:- Campaign destination tag
- Hex encoded in
Blob
payload:**VoteApprovePayload**
- The Hook is invoked on the incoming transaction.
- Hook Account parses the
Invoke
transaction- Destination Tag
- From
Blob
payload:- Transaction mode
fundTransactionId
- Hook Account parses the
- Transaction mode must be
**MODE_VOTE_APPROVE_FLAG**
in order for Vote Approve logic to be triggered.- Rollback transaction on unrecognized mode
- Hook will check if campaign state is in a milestone state (for ex.
CAMPAIGN_STATE_MILESTONE_1_FLAG
)- It will read this campaign state from Hook State to determine this.
- If it’s not, rollback the transaction
- Hook will check if current milestone has reached its end date by looking at Milestone
endDateInUnixSeconds
- If it has, update the Campaign General Info data in Hook State
- Update
state
to next campaign state - IMPORTANT: use this as the new current state
- Update
- If it has, update the Campaign General Info data in Hook State
- Hook will check if
fundTransactionId
exists in Hook State and if Sender Account is associated with it- It will read FundTransaction data from Hook State by using
fundTransactionId
as an index to the FundTransaction index range - If
fundTransactionId
index doesn’t have data or the Sender Account isn’t associated with it:- Rollback the transaction
- It will read FundTransaction data from Hook State by using
- Hook updates its Hook State
- Skip to step 8 if
state
is already in**FUND_TRANSACTION_STATE_APPROVE_FLAG**
- Update FundTransaction data
- Change
state
to**FUND_TRANSACTION_STATE_APPROVE_FLAG**
- Change
- Update Milestone data
- decrement
refundVotes
- decrement
- If majority vote (51%) of fund transactions reject a milestone, end the milestone and campaign with failed state flags
refundVotes / totalFundTransactions >= 0.51
- Update Milestone data
- update
state
to**MILESTONE_STATE_FAILED_FLAG**
- update
- Update Campaign data
- update
state
to**CAMPAIGN_STATE_FAILED_MILESTONE_FLAG**
- update
- Skip to step 8 if
- Hook accepts
Invoke
transaction
- Client submits an
- 6. Request Refund Payment
- Client submits an
Invoke
transaction to Hook Account with these fields:- Campaign destination tag
- Hex encoded in
Blob
payload:RequestRefundPaymentPayload
- The Hook is invoked on the incoming transaction.
- Hook Account parses the
Invoke
transaction- Destination Tag
- From
Blob
payload:- Transaction mode
fundTransactionId
- Hook Account parses the
- Transaction mode must be
**MODE_REQUEST_REFUND_PAYMENT_FLAG**
in order for Request Refund Payment logic to be invoked.- Rollback transaction on unrecognized mode
- Hook will read from the Hook State if the application state is in these conditions:
- Conditions
- One of these is enabled for Campaign General Info
state
:**CAMPAIGN_STATE_FAILED_FUND_RAISE_FLAG**
**CAMPAIGN_STATE_FAILED_MILESTONE_FLAG**
- FundTransaction
state
- Not set to
**FUND_TRANSACTION_STATE_REFUNDED_FLAG**
- Not set to
- One of these is enabled for Campaign General Info
- If conditions don’t meet, rollback the transaction
- Conditions
- Calculate the refund payment
- Refund based on original fund percentage of remaining campaign funds (accounting for reserve funds too)
- Equations:
**remainingFundsInDrops** = totalAmountRaisedInDrops - totalAmountRewardedInDrops
**originalFundPercentage** = FundTransaction.amountInDrops / totalAmountRaisedInDrops
**refundAmountInDrops** = **remainingFundsInDrops** * **originalFundPercentage**
- Hook emits
Payment
transaction to backer for its refund- Amount is set to
**refundAmountInDrops**
(calculated from previous step)
- Amount is set to
- Hook updates FundTransaction
state
- Set to
**FUND_TRANSACTION_STATE_REFUNDED_FLAG**
- Set to
- Hook accepts
Invoke
transaction
- Client submits an
- 7. Request Milestone Payout Payment
- Client submits an
Invoke
transaction to Hook Account with these fields:- Campaign destination tag
- Hex encoded in
Blob
payload:RequestMilestonePayoutPaymentPayload
- The Hook is invoked on the incoming transaction.
- Hook Account parses the
Invoke
transaction- Destination Tag
- From
Blob
payload:- Transaction mode
- Hook Account parses the
- Transaction mode must be
**MODE_REQUEST_MILESTONE_PAYOUT_PAYMENT_FLAG**
in order for Request Milestone Payment logic to be invoked.- Rollback transaction on unrecognized mode
- Hook will read from the Hook State if the application state is in these conditions:
- Conditions:
- Any of the milestone flags are enabled for Campaign General Info
state
, for example:**CAMPAIGN_STATE_MILESTONE_1_FLAG**
- Milestone
state
- Not set to
**MILESTONE_STATE_PAID_FLAG**
- Not set to
- Any of the milestone flags are enabled for Campaign General Info
- If conditions don’t meet, rollback the transaction
- Conditions:
- Calculate the milestone reward payment
- Reward payment based on Milestone
payoutPercent
- Equation:
**milestoneRewardAmountInDrops** = totalAmountRaisedInDrops * payoutPercent * 0.10
- Reward payment based on Milestone
- Hook emits
Payment
transaction to Campaign owner for its milestone reward- Amount is set to
**milestoneRewardAmountInDrops**
(calculated from previous step)
- Amount is set to
- Hook updates its Hook State
- Campaign General Info
- Update
totalAmountRewardedInDrops -= **milestoneRewardAmountInDrops**
- Update
- Milestone
state
- Set to
**MILESTONE_STATE_PAID_FLAG**
- Set to
- Campaign General Info
- If this is the last milestone of campaign, update Campaign General Info
state
- Set to
**CAMPAIGN_STATE_COMPLETED_FLAG**
- Set to
- Hook accepts
Invoke
transaction
- Client submits an