Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ Everytime a new release is merged into `master` there will be a new github relea

# Deploying the subgraph

The deployment manifest IPFS handler uses the native YAML API from `graph-ts` 0.38.2
and requires **Graph Node 0.37.0 or newer**. Malformed manifests retain their raw
content; derived fields that cannot be safely extracted remain unset and produce
warnings. Schema links support CIDv0 and CIDv1 in base32 or base58btc, optionally
with an IPFS file path. Unsupported links are skipped without creating a file data source.

The npm scripts are set up to deploy the subgraphs in one command. Mainnet is connected to a hook
where it will be deployed automatically when the `master` branch is updated. Therefore, we never
have to use npm scripts to directly deploy to `graph-network-mainnet`.
Expand Down Expand Up @@ -91,4 +97,4 @@ the schema.

Copyright © 2020 The Graph Foundation.

Licensed under the [MIT license](./LICENSE).
Licensed under the [MIT license](./LICENSE).
46 changes: 46 additions & 0 deletions abis/IssuanceAllocator.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
[
{
"anonymous": false,
"inputs": [
{ "indexed": true, "internalType": "address", "name": "target", "type": "address" },
{ "indexed": false, "internalType": "uint256", "name": "newAllocatorMintingRate", "type": "uint256" },
{ "indexed": false, "internalType": "uint256", "name": "newSelfMintingRate", "type": "uint256" }
],
"name": "TargetAllocationUpdated",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{ "indexed": false, "internalType": "uint256", "name": "oldIssuancePerBlock", "type": "uint256" },
{ "indexed": false, "internalType": "uint256", "name": "newIssuancePerBlock", "type": "uint256" }
],
"name": "IssuancePerBlockUpdated",
"type": "event"
},
{
"inputs": [],
"name": "getIssuancePerBlock",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [{ "internalType": "address", "name": "target", "type": "address" }],
"name": "getTargetAllocation",
"outputs": [
{
"components": [
{ "internalType": "uint256", "name": "totalAllocationRate", "type": "uint256" },
{ "internalType": "uint256", "name": "allocatorMintingRate", "type": "uint256" },
{ "internalType": "uint256", "name": "selfMintingRate", "type": "uint256" }
],
"internalType": "struct IIssuanceAllocationStatus.Allocation",
"name": "",
"type": "tuple"
}
],
"stateMutability": "view",
"type": "function"
}
]
28 changes: 27 additions & 1 deletion abis/RewardsManagerStitched.json
Original file line number Diff line number Diff line change
Expand Up @@ -603,5 +603,31 @@
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "getAllocatedIssuancePerBlock",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getIssuanceAllocator",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
}
]
]
2 changes: 2 additions & 0 deletions config/addresses.template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export class Addresses {
l2GraphTokenGateway: string
ethereumDIDRegistry: string
subgraphService: string
issuanceAllocator: string
graphPayments: string
paymentsEscrow: string
graphTallyCollector: string
Expand Down Expand Up @@ -52,6 +53,7 @@ export let addresses: Addresses = {
l2GraphTokenGateway: '{{l2GraphTokenGateway}}',
ethereumDIDRegistry: '{{ethereumDIDRegistry}}',
subgraphService: '{{subgraphService}}',
issuanceAllocator: '{{issuanceAllocator}}',
graphPayments: '{{graphPayments}}',
paymentsEscrow: '{{paymentsEscrow}}',
graphTallyCollector: '{{graphTallyCollector}}',
Expand Down
9 changes: 8 additions & 1 deletion config/arbitrumSepoliaAddressScript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Addresses } from './addresses.template'

const horizonAddresses = require('@graphprotocol/address-book/horizon/addresses.json')
const subgraphServiceAddresses = require('@graphprotocol/address-book/subgraph-service/addresses.json')
const issuanceAddresses = require('@graphprotocol/address-book/issuance/addresses.json')

// mustache doesn't like numbered object keys
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand All @@ -15,10 +16,15 @@ renameHorizonAddresses['arbsep'] = horizonAddresses['421614']
let renameSubgraphServiceAddresses: any = subgraphServiceAddresses
renameSubgraphServiceAddresses['arbsep'] = subgraphServiceAddresses['421614']

// eslint-disable-next-line @typescript-eslint/no-explicit-any
let renameIssuanceAddresses: any = issuanceAddresses
renameIssuanceAddresses['arbsep'] = issuanceAddresses['421614'] || {}

const combinedAddresses = {
arbsep: {
...renameHorizonAddresses['arbsep'],
...renameSubgraphServiceAddresses['arbsep']
...renameSubgraphServiceAddresses['arbsep'],
...renameIssuanceAddresses['arbsep']
}
}

Expand All @@ -45,6 +51,7 @@ export let addresses: Addresses = {
l2GraphTokenGateway: '{{arbsep.L2GraphTokenGateway.address}}',
ethereumDIDRegistry: '{{arbsep.EthereumDIDRegistry.address}}',
subgraphService: '{{arbsep.SubgraphService.address}}',
issuanceAllocator: '{{arbsep.IssuanceAllocator.address}}',
graphPayments: '{{arbsep.GraphPayments.address}}',
paymentsEscrow: '{{arbsep.PaymentsEscrow.address}}',
graphTallyCollector: '{{arbsep.GraphTallyCollector.address}}',
Expand Down
1 change: 1 addition & 0 deletions config/localNetworkAddressScript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Addresses } from './addresses.template'
let renameAddresses: any = {
horizon: horizonAddresses['1337'],
subgraphService: subgraphServiceAddresses['1337'],
issuanceAllocator: '0x0000000000000000000000000000000000000000', // IssuanceAllocator is only deployed on Arbitrum
}

export let addresses: Addresses = {
Expand Down
9 changes: 8 additions & 1 deletion config/mainnetArbitrumAddressScript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Addresses } from './addresses.template'

const horizonAddresses = require('@graphprotocol/address-book/horizon/addresses.json')
const subgraphServiceAddresses = require('@graphprotocol/address-book/subgraph-service/addresses.json')
const issuanceAddresses = require('@graphprotocol/address-book/issuance/addresses.json')

// mustache doesn't like numbered object keys
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand All @@ -14,10 +15,15 @@ renameHorizonAddresses['arbitrum'] = horizonAddresses['42161'] || {}
let renameSubgraphServiceAddresses: any = subgraphServiceAddresses
renameSubgraphServiceAddresses['arbitrum'] = subgraphServiceAddresses['42161'] || {}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
let renameIssuanceAddresses: any = issuanceAddresses
renameIssuanceAddresses['arbitrum'] = issuanceAddresses['42161'] || {}

const combinedAddresses = {
arbitrum: {
...renameHorizonAddresses['arbitrum'],
...renameSubgraphServiceAddresses['arbitrum']
...renameSubgraphServiceAddresses['arbitrum'],
...renameIssuanceAddresses['arbitrum']
}
}

Expand All @@ -44,6 +50,7 @@ export let addresses: Addresses = {
l2GraphTokenGateway: '{{arbitrum.L2GraphTokenGateway.address}}',
ethereumDIDRegistry: '{{arbitrum.IEthereumDIDRegistry.address}}',
subgraphService: '{{arbitrum.SubgraphService.address}}',
issuanceAllocator: '{{arbitrum.IssuanceAllocator.address}}',
graphPayments: '{{arbitrum.GraphPayments.address}}',
paymentsEscrow: '{{arbitrum.PaymentsEscrow.address}}',
graphTallyCollector: '{{arbitrum.GraphTallyCollector.address}}',
Expand Down
1 change: 1 addition & 0 deletions config/testAddressesL1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export let addresses: Addresses = {
l2GraphTokenGateway: '',
ethereumDIDRegistry: '0x0000000000000000000000000000000000000000',
subgraphService: '0x0000000000000000000000000000000000000000',
issuanceAllocator: '0x0000000000000000000000000000000000000000',
graphPayments: '0x0000000000000000000000000000000000000000',
paymentsEscrow: '0x0000000000000000000000000000000000000000',
graphTallyCollector: '0x0000000000000000000000000000000000000000',
Expand Down
1 change: 1 addition & 0 deletions config/testAddressesL2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export let addresses: Addresses = {
l2GraphTokenGateway: '0x0000000000000000000000000000000000000000',
ethereumDIDRegistry: '0x0000000000000000000000000000000000000000',
subgraphService: '0x0000000000000000000000000000000000000000',
issuanceAllocator: '0x0000000000000000000000000000000000000000',
graphPayments: '0x0000000000000000000000000000000000000000',
paymentsEscrow: '0x0000000000000000000000000000000000000000',
graphTallyCollector: '0x0000000000000000000000000000000000000000',
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@
"prepare:test-l2": "yarn && yarn prep:addresses:test:l2 && mustache ./config/generatedAddresses.json subgraph.template.yaml > subgraph.yaml && graph codegen --output-dir src/types/"
},
"devDependencies": {
"@graphprotocol/address-book": "1.1.0",
"@graphprotocol/address-book": "^1.3.0",
"@graphprotocol/contracts": "6.2.0",
"@graphprotocol/graph-cli": "0.97.0",
"@graphprotocol/graph-ts": "0.36.0",
"@graphprotocol/graph-ts": "0.38.2",
"@types/node": "^14.0.13",
"@typescript-eslint/eslint-plugin": "^3.3.0",
"@typescript-eslint/parser": "^3.3.0",
Expand Down
128 changes: 128 additions & 0 deletions src/mappings/helpers/manifest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { BigInt, YAMLValue } from '@graphprotocol/graph-ts'

// Avoid YAMLValue's [] accessor: it asserts when a key is absent.
export function yamlField(value: YAMLValue | null, key: string): YAMLValue | null {
if (value === null || !value.isObject()) return null
return value.toObject().get(YAMLValue.newString(key))
}

export function yamlString(value: YAMLValue | null): string | null {
if (value === null || !value.isString()) return null
let text = value.toString().trim()
return text.length > 0 ? text : null
}

// Block numbers must be unsigned integers. YAML NUMBER also includes floats,
// and YAMLValue.toBigInt() does not protect against invalid numeric strings.
export function manifestStartBlock(value: YAMLValue): BigInt | null {
let text: string
if (value.isNumber()) {
text = value.toNumber()
} else if (value.isString()) {
text = value.toString().trim()
} else {
return null
}
if (text.length == 0 || text.length > 20) return null
for (let i = 0; i < text.length; i++) {
let code = text.charCodeAt(i)
if (code < 48 || code > 57) return null
}
if (text.length == 20 && text > '18446744073709551615') return null
return BigInt.fromString(text)
}

// Decode the CID encodings normally used in published manifests. Unsupported
// encodings are skipped rather than passed to a host function that can abort.
function decodeCid(text: string): Uint8Array | null {
if (text.length == 0 || text.length > 128) return null
let base58 = text.startsWith('Qm') || text.startsWith('z')
if (base58) {
let encoded = text.startsWith('z') ? text.slice(1) : text
let alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
let bytes = new Array<u8>()
for (let i = 0; i < encoded.length; i++) {
let carry = alphabet.indexOf(encoded.charAt(i))
if (carry < 0) return null
for (let j = 0; j < bytes.length; j++) {
carry += i32(bytes[j]) * 58
bytes[j] = u8(carry & 255)
carry >>= 8
}
while (carry > 0) {
bytes.push(u8(carry & 255))
carry >>= 8
}
}
for (let i = 0; i < encoded.length && encoded.charAt(i) == '1'; i++) bytes.push(0)
let result = new Uint8Array(bytes.length)
for (let i = 0; i < bytes.length; i++) result[i] = bytes[bytes.length - i - 1]
return result
}
if (!text.startsWith('b') && !text.startsWith('B')) return null
let alphabet = text.startsWith('b') ? 'abcdefghijklmnopqrstuvwxyz234567' : 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'
let result = new Uint8Array((text.length - 1) * 5 / 8)
let bits = 0
let buffer = 0
let offset = 0
for (let i = 1; i < text.length; i++) {
let digit = alphabet.indexOf(text.charAt(i))
if (digit < 0) return null
buffer = (buffer << 5) | digit
bits += 5
if (bits >= 8) {
bits -= 8
result[offset++] = u8(buffer >> bits)
buffer &= (1 << bits) - 1
}
}
return bits < 5 && buffer == 0 ? result : null
}

function validCid(text: string): bool {
let bytes = decodeCid(text)
if (bytes === null) return false
if (text.startsWith('Qm')) {
return text.length == 46 && bytes.length == 34 && bytes[0] == 0x12 && bytes[1] == 0x20
}
// CIDv1 contains four unsigned varints: version, codec, hash code, digest
// length; followed by the digest. Graph Node supports digests up to 64 bytes.
let offset = 0
for (let field = 0; field < 4; field++) {
let value: u64 = 0
let terminated = false
for (let i = 0; i < 10 && offset < bytes.length; i++) {
let byte = bytes[offset++]
if (i == 9 && byte > 1) return false
value |= u64(byte & 127) << (i * 7)
if ((byte & 128) == 0) {
if (i > 0 && byte == 0) return false
terminated = true
break
}
}
if (!terminated) return false
if (field == 0 && value != 1) return false
if (field == 3) return value <= 64 && value == u64(bytes.length - offset)
}
return false
}

export function manifestSchemaPath(value: YAMLValue | null): string | null {
// Published manifests use { '/': '/ipfs/CID' }; also accept string links.
if (value !== null && value.isObject()) value = yamlField(value, '/')
let path = yamlString(value)
if (path === null || path.length > 2048) return null
if (path.startsWith('/ipfs/')) path = path.slice(6)
else if (path.startsWith('ipfs://')) path = path.slice(7)
let segments = path.split('/')
if (!validCid(segments[0])) return null
for (let i = 1; i < segments.length; i++) {
if (segments[i].length == 0 || segments[i] == '.' || segments[i] == '..') return null
}
for (let i = 0; i < path.length; i++) {
let code = path.charCodeAt(i)
if (code <= 32 || code == 127 || path.charAt(i) == '?' || path.charAt(i) == '#' || path.charAt(i) == '%' || path.charAt(i) == '\\') return null
}
return path
}
Loading
Loading