\n\n
COMP is an ERC-20 token that allows the owner to delegate voting rights to any address, including their own address. Changes to the owner’s token balance automatically adjust the voting rights of the delegate.
\n\nDelegate votes from the sender to the delegatee. Users can delegate to 1 address at a time, and the number of votes added to the delegatee’s vote count is equivalent to the balance of COMP in the user’s account. Votes are delegated from the current block and onward, until the sender delegates again, or transfers their COMP.
\n\nfunction delegate(address delegatee)\n
\n\ndelegatee
: The address in which the sender wishes to delegate their votes to.msg.sender
: The address of the COMP token holder that is attempting to delegate their votes.RETURN
: No return, reverts on error.Comp comp = Comp(0x123...); // contract address\ncomp.delegate(delegateeAddress);\n
\n\nconst tx = await comp.methods.delegate(delegateeAddress).send({ from: sender });\n
\n\nDelegate votes from the signatory to the delegatee. This method has the same purpose as Delegate but it instead enables offline signatures to participate in Compound governance vote delegation. For more details on how to create an offline signature, review EIP-712.
\n\nfunction delegateBySig(address delegatee, uint nonce, uint expiry, uint8 v, bytes32 r, bytes32 s)\n
\n\ndelegatee
: The address in which the sender wishes to delegate their votes to.nonce
: The contract state required to match the signature. This can be retrieved from the contract’s public nonces mapping.expiry
: The time at which to expire the signature. A block timestamp as seconds since the unix epoch (uint).v
: The recovery byte of the signature.r
: Half of the ECDSA signature pair.s
: Half of the ECDSA signature pair.RETURN
: No return, reverts on error.Comp comp = Comp(0x123...); // contract address\ncomp.delegateBySig(delegateeAddress, nonce, expiry, v, r, s);\n
\n\nconst tx = await comp.methods.delegateBySig(delegateeAddress, nonce, expiry, v, r, s).send({});\n
\n\nGets the balance of votes for an account as of the current block.
\n\nfunction getCurrentVotes(address account) returns (uint96)\n
\n\naccount
: Address of the account in which to retrieve the number of votes.RETURN
: The number of votes (integer).Comp comp = Comp(0x123...); // contract address\nuint votes = comp.getCurrentVotes(0xabc...);\n
\n\nconst account = '0x123...'; // contract address\nconst votes = await comp.methods.getCurrentVotes(account).call();\n
\n\nGets the prior number of votes for an account at a specific block number. The block number passed must be a finalized block or the function will revert.
\n\nfunction getPriorVotes(address account, uint blockNumber) returns (uint96)\n
\n\naccount
: Address of the account in which to retrieve the prior number of votes.blockNumber
: The block number at which to retrieve the prior number of votes.RETURN
: The number of prior votes.Comp comp = Comp(0x123...); // contract address\nuint priorVotes = comp.getPriorVotes(account, blockNumber);\n
\n\nconst priorVotes = await comp.methods.getPriorVotes(account, blockNumber).call();\n
\n\nEvent | \nDescription | \n
---|---|
DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate) | \n An event thats emitted when an account changes its delegate. | \n
DelegateVotesChanged(address indexed delegate, uint previousBalance, uint newBalance) | \n An event thats emitted when a delegate account’s vote balance changes. | \n
ProposalCreated(uint id, address proposer, address[] targets, uint[] values, string[] signatures, bytes[] calldatas, uint startBlock, uint endBlock, string description) | \n An event emitted when a new proposal is created. | \n
VoteCast(address voter, uint proposalId, bool support, uint votes) | \n An event emitted when a vote has been cast on a proposal. | \n
ProposalCanceled(uint id) | \n An event emitted when a proposal has been canceled. | \n
ProposalQueued(uint id, uint eta) | \n An event emitted when a proposal has been queued in the Timelock. | \n
ProposalExecuted(uint id) | \n An event emitted when a proposal has been executed in the Timelock. | \n
Governor Bravo is the governance module of the protocol; it allows addresses with more than 25,000 COMP to propose changes to the protocol. Addresses that held voting weight, at the start of the proposal, invoked through the getpriorvotes function, can submit their votes during a 3 day voting period. If a majority, and at least 400,000 votes are cast for the proposal, it is queued in the Timelock, and can be implemented after 2 days.
\n\nThe required minimum number of votes in support of a proposal for it to succeed.
\n\nfunction quorumVotes() public pure returns (uint)\n
\n\nRETURN
: The minimum number of votes required for a proposal to succeed.GovernorBravo gov = GovernorBravo(0x123...); // contract address\nuint quorum = gov.quorumVotes();\n
\n\nconst quorum = await gov.methods.quorumVotes().call();\n
\n\nThe minimum number of votes required for an account to create a proposal. This can be changed through governance.
\n\nfunction proposalThreshold() returns (uint)\n
\n\nRETURN
: The minimum number of votes required for an account to create a proposal.GovernorBravo gov = GovernorBravo(0x123...); // contract address\nuint threshold = gov.proposalThreshold();\n
\n\nconst threshold = await gov.methods.proposalThreshold().call();\n
\n\nThe maximum number of actions that can be included in a proposal. Actions are functions calls that will be made when a proposal succeeds and executes.
\n\nfunction proposalMaxOperations() returns (uint)\n
\nRETURN
: The maximum number of actions that can be included in a proposal.GovernorBravo gov = GovernorBravo(0x123...); // contract address\nuint operations = gov.proposalMaxOperations();\n
\n\nconst operations = await gov.methods.proposalMaxOperations().call();\n
\n\nThe number of Ethereum blocks to wait before voting on a proposal may begin. This value is added to the current block number when a proposal is created. This can be changed through governance.
\n\nfunction votingDelay() returns (uint)\n
\nRETURN
: Number of blocks to wait before voting on a proposal may begin.GovernorBravo gov = GovernorBravo(0x123...); // contract address\nuint blocks = gov.votingDelay();\n
\n\nconst blocks = await gov.methods.votingDelay().call();\n
\n\nThe duration of voting on a proposal, in Ethereum blocks. This can be changed through governance.
\n\nfunction votingPeriod() returns (uint)\n
\n\nRETURN
: The duration of voting on a proposal, in Ethereum blocks.GovernorBravo gov = GovernorBravo(0x123...); // contract address\nuint blocks = gov.votingPeriod();\n
\n\nconst blocks = await gov.methods.votingPeriod().call();\n
\n\nCreate a Proposal to change the protocol. E.g., A proposal can set a cToken’s interest rate model or risk parameters on the Comptroller.\nProposals will be voted on by delegated voters. If there is sufficient support before the voting period ends, the proposal shall be automatically enacted. Enacted proposals are queued and executed in the Compound Timelock contract.
\n\nThe sender must hold more COMP than the current proposal threshold (proposalThreshold()
) as of the immediately previous block. If the threshold is 25,000 COMP, the sender must have been delegated more than 1% of all COMP in order to create a proposal. The proposal can have up to 10 actions (based on proposalMaxOperations()
).
The proposer cannot create another proposal if they currently have a pending or active proposal. It is not possible to queue two identical actions in the same block (due to a restriction in the Timelock), therefore actions in a single proposal must be unique, and unique proposals that share an identical action must be queued in different blocks.
\n\nfunction propose(address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas, string memory description) returns (uint)\n
\n\ntargets
: The ordered list of target addresses for calls to be made during proposal execution. This array must be the same length as all other array parameters in this function.values
: The ordered list of values (i.e. msg.value) to be passed to the calls made during proposal execution. This array must be the same length as all other array parameters in this function.signatures
: The ordered list of function signatures to be passed during execution. This array must be the same length as all other array parameters in this function.calldatas
: The ordered list of data to be passed to each individual function call during proposal execution. This array must be the same length as all other array parameters in this function.description
: A human readable description of the proposal and the changes it will enact.RETURN
: The ID of the newly created proposal.GovernorBravo gov = GovernorBravo(0x123...); // contract address\nuint proposalId = gov.propose(targets, values, signatures, calldatas, description);\n
\n\nconst tx = gov.methods.propose(targets, values, signatures, calldatas, description).send({ from: sender });\n
\n\nAfter a proposal has succeeded, it is moved into the Timelock waiting period using this function. The waiting period (e.g. 2 days) begins when this function is called.\nThe queue function can be called by any Ethereum address.
\n\nfunction queue(uint proposalId)\n
\nproposalId
: ID of a proposal that has succeeded.RETURN
: No return, reverts on error.GovernorBravo gov = GovernorBravo(0x123...); // contract address\ngov.queue(proposalId);\n
\n\nconst tx = gov.methods.queue(proposalId).send({ from: sender });\n
\n\nAfter the Timelock waiting period has elapsed, a proposal can be executed using this function, which applies the proposal changes to the target contracts. This will invoke each of the actions described in the proposal.\nThe execute function can be called by any Ethereum address.\nNote: this function is payable, so the Timelock contract can invoke payable functions that were selected in the proposal.
\n\nfunction execute(uint proposalId) payable\n
\n\nproposalId
: ID of a succeeded proposal to execute.RETURN
: No return, reverts on error.GovernorBravo gov = GovernorBravo(0x123...); // contract address\ngov.execute(proposalId).value(999).gas(999)();\n
\n\nconst tx = gov.methods.execute(proposalId).send({ from: sender, value: 1 });\n
\n\nA proposal is eligible to be cancelled at any time prior to its execution, including while queued in the Timelock, using this function.
\n\nThe cancel function can be called by the proposal creator, or any Ethereum address, if the proposal creator fails to maintain more delegated votes than the proposal threshold (e.g. 25,000).
\n\nfunction cancel(uint proposalId)\n
\n\nproposalId
: ID of a proposal to cancel. The proposal cannot have already been executed.RETURN
: No return, reverts on error.GovernorBravo gov = GovernorBravo(0x123...); // contract address\ngov.cancel(proposalId);\n
\n\nconst tx = gov.methods.cancel(proposalId).send({ from: sender });\n
\n\nGets the actions of a selected proposal. Pass a proposal ID and get the targets, values, signatures and calldatas of that proposal.
\n\nfunction getActions(uint proposalId) returns (uint proposalId) public view returns (address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas)\n
\n\nproposalId
: ID of a proposal in which to get its actions.RETURN
: Reverts if the proposal ID is invalid. If successful, the following 4 references are returned.\n GovernorBravo gov = GovernorBravo(0x123...); // contract address\nuint proposalId = 123;\n(address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas) = gov.getActions(proposalId);\n
\n\nconst {0: targets, 1: values, 2: signatures, 3: calldatas} = gov.methods.getActions(proposalId).call();\n
\n\nGets a proposal ballot receipt of the indicated voter.
\n\nfunction getReceipt(uint proposalId, address voter) returns (Receipt memory)\n
\n\nproposalId
: ID of the proposal in which to get a voter’s ballot receipt.voter
: Address of the account of a proposal voter.RETURN
: Reverts on error. If successful, returns a Receipt struct for the ballot of the voter address.GovernorBravo gov = GovernorBravo(0x123...); // contract address\nReceipt ballot = gov.getReceipt(proposalId, voterAddress);\n
\n\nconst proposalId = 11;\nconst voterAddress = '0x123...';\nconst result = await gov.methods.getReceipt(proposalId, voterAddress).call();\nconst { hasVoted, support, votes } = result;\n
\n\nGets the proposal state for the specified proposal. The return value, ProposalState
is an enumerated type defined in the Governor Bravo contract.
function state(uint proposalId) returns (ProposalState)\n
\n\nproposalId
: ID of a proposal in which to get its state.RETURN
: Enumerated type ProposalState. The types are Pending, Active, Canceled, Defeated, Succeeded, Queued, Expired, andExecuted.GovernorBravo gov = GovernorBravo(0x123...); // contract address\nGovernorBravo.ProposalState state = gov.state(123);\n
\n\nconst proposalStates = ['Pending', 'Active', 'Canceled', 'Defeated', 'Succeeded', 'Queued', 'Expired', 'Executed'];\nconst proposalId = 123;\nresult = await gov.methods.state(proposalId).call();\nconst proposalState = proposalStates[result];\n
\n\nCast a vote on a proposal. The account’s voting weight is determined by the number of votes the account had delegated to it at the time the proposal state became active.
\n\nfunction castVote(uint proposalId, uint8 support)\n
\n\nproposalId
: ID of a proposal in which to cast a vote.support
: An integer of 0 for against, 1 for in-favor, and 2 for abstain.RETURN
: No return, reverts on error.GovernorBravo gov = GovernorBravo(0x123...); // contract address\ngov.castVote(proposalId, 1);\n
\n\nconst tx = gov.methods.castVote(proposalId, 0).send({ from: sender });\n
\n\nCast a vote on a proposal with a reason attached to the vote.
\n\nfunction castVoteWithReason(uint proposalId, uint8 support, string calldata reason)\n
\n\nproposalId
: ID of a proposal in which to cast a vote.support
: An integer of 0 for against, 1 for in-favor, and 2 for abstain.reason
: A string containing the voter’s reason for their vote selection.RETURN
: No return, reverts on error.GovernorBravo gov = GovernorBravo(0x123...); // contract address\ngov.castVoteWithReason(proposalId, 2, \"I think...\");\n
\n\nconst tx = gov.methods.castVoteWithReason(proposalId, 0, \"I think...\").send({ from: sender });\n
\n\nCast a vote on a proposal. The account’s voting weight is determined by the number of votes the account had delegated at the time that proposal state became active. This method has the same purpose as Cast Vote but it instead enables offline signatures to participate in Compound governance voting. For more details on how to create an offline signature, review EIP-712.
\n\nfunction castVoteBySig(uint proposalId, uint8 support, uint8 v, bytes32 r, bytes32 s)\n
\n\nproposalId
: ID of a proposal in which to cast a vote.support
: An integer of 0 for against, 1 for in-favor, and 2 for abstain.v
: The recovery byte of the signature.r
: Half of the ECDSA signature pair.s
: Half of the ECDSA signature pair.RETURN
: No return, reverts on error.GovernorBravo gov = GovernorBravo(0x123...); // contract address\ngov.castVoteBySig(proposalId, 0, v, r, s);\n
\n\nconst tx = await gov.methods.castVoteBySig(proposalId, 1, v, r, s).send({});\n
\n\nEach protocol contract is controlled by the Timelock contract, which can modify system parameters, logic, and contracts in a ‘time-delayed, opt-out’ upgrade pattern. The Timelock has a hard-coded minimum delay which is the least amount of notice possible for a governance action.\nThe Timelock contract queues and executes proposals that have passed a Governance vote.
\n\nThe Comptroller contract designates a Pause Guardian address capable of disabling protocol functionality. Used only in the event of an unforeseen vulnerability, the Pause Guardian has one and only one ability: to disable a select set of functions: Mint, Borrow, Transfer, and Liquidate. The Pause Guardian cannot unpause an action, nor can it ever prevent users from calling Redeem, or Repay Borrow to close positions and exit the protocol.\nCOMP token-holders designate the Pause Guardian address, which is held by the Community Multi-Sig.
\n", "section_labels": [{ "id": "governance", "name": "Governance" },{ "id": "comp", "name": "COMP" },{ "id": "delegate", "name": "Delegate" },{ "id": "delegate-by-signature", "name": "Delegate By Signature" },{ "id": "get-current-votes", "name": "Get Current Votes" },{ "id": "get-prior-votes", "name": "Get Prior Votes" },{ "id": "key-events", "name": "Key Events" },{ "id": "governor-bravo", "name": "Governor Bravo" },{ "id": "quorum-votes", "name": "Quorum Votes" },{ "id": "proposal-threshold", "name": "Proposal Threshold" },{ "id": "proposal-max-operations", "name": "Proposal Max Operations" },{ "id": "voting-delay", "name": "Voting Delay" },{ "id": "voting-period", "name": "Voting Period" },{ "id": "propose", "name": "Propose" },{ "id": "queue", "name": "Queue" },{ "id": "execute", "name": "Execute" },{ "id": "cancel", "name": "Cancel" },{ "id": "get-actions", "name": "Get Actions" },{ "id": "get-receipt", "name": "Get Receipt" },{ "id": "state", "name": "State" },{ "id": "cast-vote", "name": "Cast Vote" },{ "id": "cast-vote-with-reason", "name": "Cast Vote With Reason" },{ "id": "cast-vote-by-signature", "name": "Cast Vote By Signature" },{ "id": "timelock", "name": "Timelock" },{ "id": "pause-guardian", "name": "Pause Guardian" },], },{ "title" : "Compound v2 Docs | Price Feed", "url" : "/v2/prices/", "raw_html" : "The Compound v2 Price Feed accounts price data for the protocol. The protocol’s Comptroller contract uses it as a source of truth for prices. Prices are updated by Chainlink Price Feeds. The codebase is hosted on GitHub, and maintained by the community.
\n\nThe Compound v2 Price Feed fetches prices directly from Chainlink price feeds for valid cTokens when requested. Price feed addresses can be updated by the Compound multisig. The price feeds configured for each cToken can be found below:
\n\nContract name | \naddress | \n
---|---|
AAVE Price Feed | \n0x547a514d5e3769680ce22b2361c10ea13619e8a9 | \n
BAT Price Feed | \n0x9441D7556e7820B5ca42082cfa99487D56AcA958 | \n
COMP Price Feed | \n0xdbd020caef83efd542f4de03e3cf0c28a4428bd5 | \n
DAI Price Feed | \n0xaed0c38402a5d19df6e4c03f4e2dced6e29c1ee9 | \n
ETH Price Feed | \n0x5f4ec3df9cbd43714fe2740f5e3616155c5b8419 | \n
LINK Price Feed | \n0x2c1d072e956affc0d435cb7ac38ef18d24d9127c | \n
MKR Price Feed | \n0xec1d1b3b0443256cc3860e24a46f108e699484aa | \n
SUSHI Price Feed | \n0xcc70f09a6cc17553b2e31954cd36e4a2d89501f7 | \n
UNI Price Feed | \n0x553303d460ee0afb37edff9be42922d8ff63220e | \n
WBTC Price Feed | \n0x45939657d1CA34A8FA39A924B71D28Fe8431e581 | \n
YFI Price Feed | \n0xa027702dbb89fbd58938e4324ac03b58d812b0e1 | \n
ZRX Price Feed | \n0x2885d15b8af22648b98b122b22fdf4d2a56c6023 | \n
Price Feed | \n0x8CF42B08AD13761345531b839487aA4d113955d9 | \n
The Compound v2 Price Feed also supports fixed prices for cTokens. This feature is required for cTokens that do not have a price feed. One such scenario is when a cToken is deprecated but the price at deprecation still needs to be reported. Below are cTokens using fixed prices:
\n\ncToken | \nFixed Price | \n
---|---|
FEI | \n1001094000000000000 | \n
REP | \n6433680000000000000 | \n
SAI | \n14426337000000000000 | \n
The Compound v2 Price Feed refers to a single contract.
\nPriceOracle
fetches prices from Chainlink Price Feeds when requested for a specific cToken. Also contains logic that upscales the fetched price into the format that Compound’s Comptroller expects. The code is accessible on GitHub.The Compound community multisig has the ability to update the configs on the Price Feed. The multisig has the flexibility to make the following changes:
\n\naddConfig
function enables the multisig to add a new cToken with an associated price feed or fixed price to support new markets.updateConfigPriceFeed
function enables the multisig to update the price feed for an existing cToken. It can also be used to switch the configs from fixed price to a price feed.updateConfigFixedPrice
enables the multisig to update the fixed price for an existing cToken. It can also be used to switch the configs from a price feed to a fixed price such as in the case of deprecation.removeConfig
function enables the multisig to remove configs for a deprecated market that the Comptroller no longer needs prices for.Get the most recent price for a token in USD with the Chainlink price feed’s decimals of precision.
\n\ncToken
: The address of the cToken contract of the underlying asset.RETURNS
: The price of the asset in USD as an unsigned integer scaled up by 10 ^ (36 - underlying asset decimals)
. E.g. WBTC has 8 decimal places, so the return value is scaled up by 1e28
.function getUnderlyingPrice(address cToken) external view returns (uint)\n
\n\nPriceFeed view = PriceFeed(0xABCD...);\nuint price = view.getUnderlyingPrice(0x1230...);\n
\n\nconst view = PriceFeed.at(\"0xABCD...\");\n//eg: returns 400e6\nconst price = await view.methods.getUnderlyingPrice(\"0x1230...\").call();\n
\n\nEach token the Compound v2 Price Feed supports needs corresponding configuration metadata. The configuration for each token is stored in the Price Feed contract and can be updated by the multisig.
\n\nThe fields of the config are:
\n\ncToken
: The address of the underlying token’s corresponding cToken. This is a required field.underlyingAssetDecimals
: The decimals of the underlying asset. E.g. 18 for ETH. This field is required when using price feeds but optional when using fixed price.priceFeed
: The address of the Chainlink price feed. This field is set to the 0 address when using a fixed price.fixedPrice
: The fixed dollar amount to use as the price. This field is set to 0 if a price feed is being used.function getConfig(address cToken) external view returns (TokenConfig memory)\n
\n\nconst view = PriceFeed.at(\"0xABCD...\");\nconst config = await view.methods.getConfig(\"0x1230...\").call();\n
\n\nPriceFeed view = PriceFeed(0xABCD...);\nTokenConfig memory config = view.getConfig(\"0x1230...\");\n
\n", "section_labels": [{ "id": "introduction", "name": "Introduction" },{ "id": "architecture", "name": "Architecture" },{ "id": "underlying-price", "name": "Get Underlying Price" },{ "id": "config", "name": "Get Config" },], },{ "title" : "Compound v2 Docs | Security", "url" : "/v2/security/", "raw_html" : "The security of the Compound protocol is our highest priority; our development team, alongside third-party auditors and consultants, has invested considerable effort to create a protocol that we believe is safe and dependable. All contract code and balances are publicly verifiable, and security researchers are eligible for a bug bounty for reporting undiscovered vulnerabilities.
\n\nWe believe that size, visibility, and time are the true test for the security of a smart contract; please exercise caution, and make your own determination of security and suitability.
\n\nThe Compound protocol has been reviewed & audited by Trail of Bits and OpenZeppelin.
\n\nThe Compound protocol was developed with a specifications of security principles, and formally verified by Certora using Certora ASA (Accurate Static Analysis), which is integrated into Compound’s continuous integration system.
\n\n01 Certora Verification Summary
\n\nGauntlet has constructed a simulation-based market stress-testing platform to evaluate the economic security of the Compound protocol, as it scales supported assets and volume.
\n\n01 Gauntlet Market Risk Assessment
\n\nSecurity is core to our values, and we value the input of hackers acting in good faith to help us maintain the highest standard for the security and safety of the Ethereum ecosystem. The Compound protocol, while it has gone through professional audits and formal verification, depends on new technology that may contain undiscovered vulnerabilities.
\n\nCompound encourages the community to audit our contracts and security; we also encourage the responsible disclosure of any issues. This program is intended to recognize the value of working with the community of independent security researchers, and sets out our definition of good faith in the context of finding and reporting vulnerabilities, as well as what you can expect from us in return.
\n\nCompound offers substantial rewards for discoveries that can prevent the loss of assets, the freezing of assets, or harm to a user, commensurate with the severity and exploitability of the vulnerability. Compound will pay a reward of $500 to $150,000 for eligible discoveries according to the terms and conditions provided below.
\n\nThe primary scope of the bug bounty program is for vulnerabilities affecting the on-chain Compound Protocol, deployed to the Ethereum Mainnet, for contract addresses listed in this developer documentation.
\n\nThis list may change as new contracts are deployed, or as existing contracts are removed from usage. Vulnerabilities in contracts built on top of the Protocol by third-party developers (such as smart contract wallets) are not in-scope, nor are vulnerabilities that require ownership of an admin key.
\n\nThe secondary scope of the bug bounty program is for vulnerabilities affecting the Compound Interface hosted at app.compound.finance that could conceivably result in exploitation of user accounts.
\n\nFinally, test contracts (Rinkeby and other testnets) and staging servers are out of scope, unless the discovered vulnerability also affects the Compound Protocol or Interface, or could otherwise be exploited in a way that risks user funds.
\n\nSubmit all bug bounty disclosures to security@compound.finance. The disclosure must include clear and concise steps to reproduce the discovered vulnerability in either written or video format. Compound will follow up promptly with acknowledgement of the disclosure.
\n\nTo be eligible for bug bounty reward consideration, you must:
\n\nTo encourage vulnerability research and to avoid any confusion between good-faith hacking and malicious attack, we require that you:
\n\nWhen working with us according to this program, you can expect us to:
\n\nAll reward determinations, including eligibility and payment amount, are made at Compound’s sole discretion. Compound reserves the right to reject submissions and alter the terms and conditions of this program.
\n", "section_labels": [{ "id": "security", "name": "Security" },{ "id": "audits", "name": "Audits" },{ "id": "formal-verification", "name": "Formal Verification" },{ "id": "economic-security", "name": "Economic Security" },{ "id": "bug-bounty-program", "name": "Bug Bounty" },], },]; /* Format search data from all Jekyll pages so it can be searched */ searchData.forEach((page, i) => { let remainder = page.raw_html; searchData[i].section_contents = []; if (page.section_labels.length > 0) { page.section_labels.forEach((sectionLabel, j) => { const { id, name } = sectionLabel; const split = remainder.split(` id="${id}"`); searchData[i].section_contents.push(stripHtml(split[0])); if (j === page.section_labels.length - 1) { /* Add the last section at the end of the array */ searchData[i].section_contents.push(stripHtml(split[1])); } else { /* Queue up the HTML string to the next section */ remainder = split[1]; } }); searchData[i].section_contents.shift(); /* first one should always be empty */ } }); function instantSearch(input) { if (input.length < 4) { return; } input = input.toLowerCase(); const results = []; searchData.forEach((page, j) => { if (page.section_contents.length > 0) { page.section_contents.forEach((text, i) => { text = text.toLowerCase(); /* https://stackoverflow.com/a/58828841 */ const result = [...text.matchAll(new RegExp(input, 'gi'))].map(a => a.index); if (result.length > 0) { const { id, name } = page.section_labels[i]; /* Prioritize exact section name matches */ const bonus = name.toLowerCase() === input ? Infinity : 0; results.push({ section_name: name, score: result.length + bonus, href: page.url + '#' + id, context: text.substring(result[0] - 50, result[0] + 100).replace(/[^a-zA-Z0-9\s]/g, ''), }); } }); } }); results.sort((a, b) => { return a.score > b.score ? -1 : 1; }); globalSearchDataArray = results.length > 0 ? results : globalSearchDataArray; } const active = 'active'; let resultsAreShowing = false; let globalSearchDataArray = []; let activeResultIndex = 0; /* Add the results to the DOM as the user types in the search box */ function updateSearchResultUi(input) { if (globalSearchDataArray.length > 0) { input = input.toLowerCase(); globalSearchDataArray = globalSearchDataArray.slice(0, 6); /* maximum of 6 results show */ const resultsContainer = document.getElementById('search-results'); resultsContainer.innerHTML = ''; resultsContainer.classList.add(active); globalSearchDataArray.forEach((result) => { const emphasisInjectedHtml = result.context.replace(new RegExp(input, 'g'), `Contract | Address |
---|