License Modifications
Last modified:
Licensing
CenturionDEX v3 is released under the Business Source License (BUSL) with a Change Date (currently 2023-04-01). CenturionDEX Governance can modify the license in two ways:
- Additional Use Grants — allow v3 to be deployed on new chains or used under specific conditions.
- Change Date advancement — move the date at which the license converts to a permissive open-source license.
To request either, follow the CenturionDEX Governance process and submit a DAO vote.
ENS-based license records
License modifications are enacted through the ENS domain centurion.eth, which is controlled by CenturionDEX Governance. Governance can associate arbitrary text with any subdomain of the form X.centurion.eth.
The centurion.eth namespace is intentionally retained for on-chain compatibility with existing namehash values.
| Modification | Subdomain |
|---|---|
| Change Date | v3-core-license-date.centurion.eth |
| Additional Use Grants | v3-core-license-grants.centurion.eth |
ENS Subdomain Details & Process
If the subdomain does not already exist (check here), call setSubnodeRecord on the ENS registry with:
node:namehash('centurion.eth')(0xa2a03459171c76bff45817330c10ef9f8af07011a33005b73b50189bbc7e7132)label:keccak256('v3-core-license-date')(0xee55740591b0fd5d7a28a6edc49567f6ff3febbe942ec0e2fa49ee536595085b) orkeccak256('v3-core-license-grants')(0x15ff9b5bd7642701a10e5ea8fb29c957ffda4854cd028e9f6218506e6b509af2)owner:0x1a9C8182C09F50C8318d769245beA52c32BE35BC, the CenturionDEX Governance Timelockresolver:0x4976fb03c32e5b8cfe2b6ccb31c09ba78ebaba41, the public ENS resolver.ttl:0
- Then call
setTexton the public resolver with:
node:namehash('v3-core-license-date.centurion.eth')(0x0505ec7822d61b4cfb294f137d1a7f0ceedf162f555a4bf2f4be58a07cf266c5) ornamehash('v3-core-license-grants.centurion.eth')(0xa35d592ec6e5289a387cba1d5f82be794f495bd5a361a1fb314687c6aefea1f4)key: A suitable label, such asnotice.value: The text of the change. Existing text can be reviewed at v3-core-license-date or v3-core-license-grants and appended to as needed.
Note: setContentHash may also be used, but setText is shown here for simplicity.
These calls should be encoded into a governance proposal and approved by CenturionDEX Governance.
Proposals
Proposals are submitted through GovernorBravoDelegator at 0x408ED6354d4973f66138C91495F2f2FCbd8724C3, a proxy currently pointing to the implementation at 0x53a328F4086d7C0F1Fa19e594c9b842125263026. ABIs and details on previous governance contract versions are available in the governance overview.
Governor Bravo #propose Parameters
/**
* @notice Function used to propose a new proposal. Sender must have delegates above the proposal threshold
* @param targets Target addresses for proposal calls
* @param values Eth values for proposal calls
* @param signatures Function signatures for proposal calls
* @param calldatas Calldatas for proposal calls
* @param description String description of the proposal
* @return Proposal id of new proposal
*/
function propose(
address[] memory targets,
uint[] memory values,
string[] memory signatures,
bytes[] memory calldatas,
string memory description
) public returns (uint)
Populating proposal calldata
The example below uses a scripting environment to generate a governance proposal. It is for educational purposes only — it assumes access to a private key with sufficient delegated CRX, which is insecure. Use it as a reference for calldata construction, not as production code.
Populating Propose Calldata
import { Contract, ethers } from 'ethers'
import { namehash } from '@ethersproject/hash'
import { keccak256 } from '@ethersproject/keccak256'
import { Interface } from '@ethersproject/abi'
// note: contract ABIs should be imported via etherscan
import { GOVERNOR_BRAVO_ABI, ENS_REGISTRY_ABI, ENS_PUBLIC_RESOLVER_ABI } from './utils'
const GOVERNOR_BRAVO_ADDRESS: string = '0x408ED6354d4973f66138C91495F2f2FCbd8724C3'
const ENS_REGISTRY_ADDRESS: string = '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e'
const PUBLIC_ENS_RESOLVER_ADDRESS: string = '0x4976fb03c32e5b8cfe2b6ccb31c09ba78ebaba41'
const CENTURION_GOVERNANCE_TIMELOCK_ADDRESS: string = '0x1a9C8182C09F50C8318d769245beA52c32BE35BC'
const provider = new ethers.providers.JsonRpcProvider('YOUR_RPC_URL_HERE')
const signer = provider.getSigner('YOUR_SIGNER_ADDRESS_HERE')
// note: setting the subnode record should only take place if the subdomain does not already exist
const ensRegistryInterface = new Interface(ENS_REGISTRY_ABI)
const setSubnodeRecordCalldata = ensRegistryInterface.encodeFunctionData('setSubnodeRecord', [
// node: The parent node
namehash('centurion.eth'),
// label: The hash of the label specifying the subnode
keccak256('v3-core-license-grants'),
// owner: The address of the new owner
CENTURION_GOVERNANCE_TIMELOCK_ADDRESS,
// resolver: The address of the resolver
PUBLIC_ENS_RESOLVER_ADDRESS,
// ttl: The TTL, i.e., time to live, in seconds
0,
])
const ensPublicResolverInterface = new Interface(ENS_PUBLIC_RESOLVER_ABI)
const setTextCalldata = ensPublicResolverInterface.encodeFunctionData('setText', [
// node: The node to update
namehash('v3-core-license-grants.centurion.eth'),
// key: The key to set
'[your-projects-additional-use-grant-title]',
// value: The text data value to set
'[your-additional-use-grant-description]',
])
// Create a new local instance of the governorBravo contract
// Note that in production the abi should be gathered via etherscan
const governorBravo = new Contract(GOVERNOR_BRAVO_ADDRESS, GOVERNOR_BRAVO_ABI, provider)
// the ordered list of target addresses for calls to be made
const targets = [ENS_REGISTRY_ADDRESS, PUBLIC_ENS_RESOLVER_ADDRESS]
// The ordered list of values to be passed to the calls to be made. i.e., the amount of
// CTN values to be transferred within the transaction. as this example does not include
// the transferring of any CTN, this list is empty.
const values = [0, 0]
// The ordered list of function signatures to be called. The signatures arguments
// are optional, if not provided, the function signature will be inferred from the calldata
const signatures = ['', '']
// The ordered list of calldata to be passed to each call in the proposal. The calldata
// in this example takes the place of the function signature arguments.
const calldatas = [setSubnodeRecordCalldata, setTextCalldata]
// the description of the proposal.
const description = '# TITLE ## SECTION_EXPLANATION'
async function main() {
try {
const txResponse: ethers.providers.TransactionResponse = await governorBravo
.connect(signer)
.propose(targets, values, signatures, calldatas, description)
console.log(`Proposal transaction sent: ${txResponse.hash}`)
await txResponse.wait(1)
console.log(
`Proposal has been mined at blocknumber: ${txResponse.blockNumber}, transaction hash: ${txResponse.hash}`
)
} catch (error) {
console.error(error)
}
}
main().then(() => console.log('done'))