@geoprotocol/geo-sdk 0.18.2 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +995 -372
- package/dist/contracts.d.ts +0 -11
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js +0 -11
- package/dist/contracts.js.map +1 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/lite.d.ts +2 -0
- package/dist/lite.d.ts.map +1 -1
- package/dist/lite.js +2 -0
- package/dist/lite.js.map +1 -1
- package/dist/src/abis/dao-space-v2.test.d.ts +2 -0
- package/dist/src/abis/dao-space-v2.test.d.ts.map +1 -0
- package/dist/src/abis/dao-space-v2.test.js +67 -0
- package/dist/src/abis/dao-space-v2.test.js.map +1 -0
- package/dist/src/client/api.d.ts +86 -0
- package/dist/src/client/api.d.ts.map +1 -0
- package/dist/src/client/api.js +169 -0
- package/dist/src/client/api.js.map +1 -0
- package/dist/src/client/comments.d.ts +59 -0
- package/dist/src/client/comments.d.ts.map +1 -0
- package/dist/src/client/comments.js +96 -0
- package/dist/src/client/comments.js.map +1 -0
- package/dist/src/client/context.d.ts +20 -0
- package/dist/src/client/context.d.ts.map +1 -0
- package/dist/src/client/context.js +20 -0
- package/dist/src/client/context.js.map +1 -0
- package/dist/src/client/dao-spaces.d.ts +348 -0
- package/dist/src/client/dao-spaces.d.ts.map +1 -0
- package/dist/src/client/dao-spaces.js +494 -0
- package/dist/src/client/dao-spaces.js.map +1 -0
- package/dist/src/client/dao-spaces.test.d.ts +2 -0
- package/dist/src/client/dao-spaces.test.d.ts.map +1 -0
- package/dist/src/client/dao-spaces.test.js +263 -0
- package/dist/src/client/dao-spaces.test.js.map +1 -0
- package/dist/src/client/edits.d.ts +100 -0
- package/dist/src/client/edits.d.ts.map +1 -0
- package/dist/src/client/edits.js +131 -0
- package/dist/src/client/edits.js.map +1 -0
- package/dist/src/client/edits.test.d.ts +2 -0
- package/dist/src/client/edits.test.d.ts.map +1 -0
- package/dist/src/client/edits.test.js +98 -0
- package/dist/src/client/edits.test.js.map +1 -0
- package/dist/src/client/entities.d.ts +23 -0
- package/dist/src/client/entities.d.ts.map +1 -0
- package/dist/src/client/entities.js +88 -0
- package/dist/src/client/entities.js.map +1 -0
- package/dist/src/client/entity-votes.d.ts +141 -0
- package/dist/src/client/entity-votes.d.ts.map +1 -0
- package/dist/src/client/entity-votes.js +168 -0
- package/dist/src/client/entity-votes.js.map +1 -0
- package/dist/src/client/entity-votes.test.d.ts +2 -0
- package/dist/src/client/entity-votes.test.d.ts.map +1 -0
- package/dist/src/client/entity-votes.test.js +93 -0
- package/dist/src/client/entity-votes.test.js.map +1 -0
- package/dist/src/client/graph-workflows.test.d.ts +2 -0
- package/dist/src/client/graph-workflows.test.d.ts.map +1 -0
- package/dist/src/client/graph-workflows.test.js +83 -0
- package/dist/src/client/graph-workflows.test.js.map +1 -0
- package/dist/src/client/images-storage.test.d.ts +2 -0
- package/dist/src/client/images-storage.test.d.ts.map +1 -0
- package/dist/src/client/images-storage.test.js +52 -0
- package/dist/src/client/images-storage.test.js.map +1 -0
- package/dist/src/client/images.d.ts +35 -0
- package/dist/src/client/images.d.ts.map +1 -0
- package/dist/src/client/images.js +90 -0
- package/dist/src/client/images.js.map +1 -0
- package/dist/src/client/personal-spaces.d.ts +155 -0
- package/dist/src/client/personal-spaces.d.ts.map +1 -0
- package/dist/src/client/personal-spaces.js +198 -0
- package/dist/src/client/personal-spaces.js.map +1 -0
- package/dist/src/client/proposals.d.ts +185 -0
- package/dist/src/client/proposals.d.ts.map +1 -0
- package/dist/src/client/proposals.js +294 -0
- package/dist/src/client/proposals.js.map +1 -0
- package/dist/src/client/proposals.test.d.ts +2 -0
- package/dist/src/client/proposals.test.d.ts.map +1 -0
- package/dist/src/client/proposals.test.js +243 -0
- package/dist/src/client/proposals.test.js.map +1 -0
- package/dist/src/client/spaces.test.d.ts +2 -0
- package/dist/src/client/spaces.test.d.ts.map +1 -0
- package/dist/src/client/spaces.test.js +155 -0
- package/dist/src/client/spaces.test.js.map +1 -0
- package/dist/src/client/storage.d.ts +52 -0
- package/dist/src/client/storage.d.ts.map +1 -0
- package/dist/src/client/storage.js +53 -0
- package/dist/src/client/storage.js.map +1 -0
- package/dist/src/client.d.ts +325 -0
- package/dist/src/client.d.ts.map +1 -0
- package/dist/src/client.js +452 -0
- package/dist/src/client.js.map +1 -0
- package/dist/src/client.test.d.ts +2 -0
- package/dist/src/client.test.d.ts.map +1 -0
- package/dist/src/client.test.js +120 -0
- package/dist/src/client.test.js.map +1 -0
- package/dist/src/contracts-v2/abis.d.ts +196 -0
- package/dist/src/contracts-v2/abis.d.ts.map +1 -0
- package/dist/src/contracts-v2/abis.js +110 -0
- package/dist/src/contracts-v2/abis.js.map +1 -0
- package/dist/src/contracts-v2/actions.d.ts +56 -0
- package/dist/src/contracts-v2/actions.d.ts.map +1 -0
- package/dist/src/contracts-v2/actions.js +83 -0
- package/dist/src/contracts-v2/actions.js.map +1 -0
- package/dist/src/contracts-v2/encoding.d.ts +80 -0
- package/dist/src/contracts-v2/encoding.d.ts.map +1 -0
- package/dist/src/contracts-v2/encoding.js +193 -0
- package/dist/src/contracts-v2/encoding.js.map +1 -0
- package/dist/src/contracts-v2/encoding.test.d.ts +2 -0
- package/dist/src/contracts-v2/encoding.test.d.ts.map +1 -0
- package/dist/src/contracts-v2/encoding.test.js +93 -0
- package/dist/src/contracts-v2/encoding.test.js.map +1 -0
- package/dist/src/contracts-v2/local-geobrowser.e2e.test.d.ts +2 -0
- package/dist/src/contracts-v2/local-geobrowser.e2e.test.d.ts.map +1 -0
- package/dist/src/contracts-v2/local-geobrowser.e2e.test.js +239 -0
- package/dist/src/contracts-v2/local-geobrowser.e2e.test.js.map +1 -0
- package/dist/src/contracts-v2/voting-settings.d.ts +48 -0
- package/dist/src/contracts-v2/voting-settings.d.ts.map +1 -0
- package/dist/src/contracts-v2/voting-settings.js +69 -0
- package/dist/src/contracts-v2/voting-settings.js.map +1 -0
- package/dist/src/dao-space/constants.d.ts +6 -3
- package/dist/src/dao-space/constants.d.ts.map +1 -1
- package/dist/src/dao-space/constants.js +6 -5
- package/dist/src/dao-space/constants.js.map +1 -1
- package/dist/src/dao-space/create-space.d.ts +1 -31
- package/dist/src/dao-space/create-space.d.ts.map +1 -1
- package/dist/src/dao-space/create-space.js +5 -70
- package/dist/src/dao-space/create-space.js.map +1 -1
- package/dist/src/dao-space/execute-proposal.d.ts +1 -22
- package/dist/src/dao-space/execute-proposal.d.ts.map +1 -1
- package/dist/src/dao-space/execute-proposal.js +12 -59
- package/dist/src/dao-space/execute-proposal.js.map +1 -1
- package/dist/src/dao-space/propose-add-editor.test.js +1 -1
- package/dist/src/dao-space/propose-add-editor.test.js.map +1 -1
- package/dist/src/dao-space/propose-edit.d.ts +1 -30
- package/dist/src/dao-space/propose-edit.d.ts.map +1 -1
- package/dist/src/dao-space/propose-edit.js +12 -108
- package/dist/src/dao-space/propose-edit.js.map +1 -1
- package/dist/src/dao-space/propose-edit.test.js +8 -1
- package/dist/src/dao-space/propose-edit.test.js.map +1 -1
- package/dist/src/dao-space/propose-remove-editor.d.ts.map +1 -1
- package/dist/src/dao-space/propose-remove-editor.js +5 -5
- package/dist/src/dao-space/propose-remove-editor.js.map +1 -1
- package/dist/src/dao-space/propose-remove-editor.test.js +3 -8
- package/dist/src/dao-space/propose-remove-editor.test.js.map +1 -1
- package/dist/src/dao-space/propose-remove-member.d.ts.map +1 -1
- package/dist/src/dao-space/propose-remove-member.js +5 -5
- package/dist/src/dao-space/propose-remove-member.js.map +1 -1
- package/dist/src/dao-space/propose-request-membership.d.ts.map +1 -1
- package/dist/src/dao-space/propose-request-membership.js +4 -4
- package/dist/src/dao-space/propose-request-membership.js.map +1 -1
- package/dist/src/dao-space/propose-update-voting-settings.d.ts +8 -0
- package/dist/src/dao-space/propose-update-voting-settings.d.ts.map +1 -0
- package/dist/src/dao-space/propose-update-voting-settings.js +19 -0
- package/dist/src/dao-space/propose-update-voting-settings.js.map +1 -0
- package/dist/src/dao-space/propose-update-voting-settings.test.d.ts +2 -0
- package/dist/src/dao-space/propose-update-voting-settings.test.d.ts.map +1 -0
- package/dist/src/dao-space/propose-update-voting-settings.test.js +118 -0
- package/dist/src/dao-space/propose-update-voting-settings.test.js.map +1 -0
- package/dist/src/dao-space/vote-proposal.d.ts +1 -24
- package/dist/src/dao-space/vote-proposal.d.ts.map +1 -1
- package/dist/src/dao-space/vote-proposal.js +12 -64
- package/dist/src/dao-space/vote-proposal.js.map +1 -1
- package/dist/src/e2e/local-geobrowser.d.ts +9 -0
- package/dist/src/e2e/local-geobrowser.d.ts.map +1 -0
- package/dist/src/e2e/local-geobrowser.js +35 -0
- package/dist/src/e2e/local-geobrowser.js.map +1 -0
- package/dist/src/e2e/v2-contracts.test.d.ts +2 -0
- package/dist/src/e2e/v2-contracts.test.d.ts.map +1 -0
- package/dist/src/e2e/v2-contracts.test.js +25 -0
- package/dist/src/e2e/v2-contracts.test.js.map +1 -0
- package/dist/src/e2e-api-surface.test.d.ts +2 -0
- package/dist/src/e2e-api-surface.test.d.ts.map +1 -0
- package/dist/src/e2e-api-surface.test.js +1021 -0
- package/dist/src/e2e-api-surface.test.js.map +1 -0
- package/dist/src/e2e-flows.test.d.ts +2 -0
- package/dist/src/e2e-flows.test.d.ts.map +1 -0
- package/dist/src/e2e-flows.test.js +445 -0
- package/dist/src/e2e-flows.test.js.map +1 -0
- package/dist/src/e2e-legacy-api-surface.test.d.ts +2 -0
- package/dist/src/e2e-legacy-api-surface.test.d.ts.map +1 -0
- package/dist/src/e2e-legacy-api-surface.test.js +840 -0
- package/dist/src/e2e-legacy-api-surface.test.js.map +1 -0
- package/dist/src/encoding.d.ts +3 -0
- package/dist/src/encoding.d.ts.map +1 -1
- package/dist/src/encoding.js +3 -0
- package/dist/src/encoding.js.map +1 -1
- package/dist/src/encodings/get-create-dao-space-calldata.d.ts +3 -89
- package/dist/src/encodings/get-create-dao-space-calldata.d.ts.map +1 -1
- package/dist/src/encodings/get-create-dao-space-calldata.js +5 -95
- package/dist/src/encodings/get-create-dao-space-calldata.js.map +1 -1
- package/dist/src/encodings/get-create-personal-space-calldata.d.ts +2 -0
- package/dist/src/encodings/get-create-personal-space-calldata.d.ts.map +1 -1
- package/dist/src/encodings/get-create-personal-space-calldata.js +2 -0
- package/dist/src/encodings/get-create-personal-space-calldata.js.map +1 -1
- package/dist/src/graph/comment-utils.d.ts +4 -0
- package/dist/src/graph/comment-utils.d.ts.map +1 -1
- package/dist/src/graph/comment-utils.js +4 -0
- package/dist/src/graph/comment-utils.js.map +1 -1
- package/dist/src/graph/constants.d.ts +12 -3
- package/dist/src/graph/constants.d.ts.map +1 -1
- package/dist/src/graph/constants.js +11 -5
- package/dist/src/graph/constants.js.map +1 -1
- package/dist/src/graph/create-comment.d.ts +3 -22
- package/dist/src/graph/create-comment.d.ts.map +1 -1
- package/dist/src/graph/create-comment.js +6 -136
- package/dist/src/graph/create-comment.js.map +1 -1
- package/dist/src/graph/create-comment.test.js +19 -8
- package/dist/src/graph/create-comment.test.js.map +1 -1
- package/dist/src/graph/create-entity.d.ts +2 -0
- package/dist/src/graph/create-entity.d.ts.map +1 -1
- package/dist/src/graph/create-entity.js +2 -0
- package/dist/src/graph/create-entity.js.map +1 -1
- package/dist/src/graph/create-image.d.ts +2 -21
- package/dist/src/graph/create-image.d.ts.map +1 -1
- package/dist/src/graph/create-image.js +7 -69
- package/dist/src/graph/create-image.js.map +1 -1
- package/dist/src/graph/create-property.d.ts +2 -0
- package/dist/src/graph/create-property.d.ts.map +1 -1
- package/dist/src/graph/create-property.js +2 -0
- package/dist/src/graph/create-property.js.map +1 -1
- package/dist/src/graph/create-proposal-review.d.ts +1 -27
- package/dist/src/graph/create-proposal-review.d.ts.map +1 -1
- package/dist/src/graph/create-proposal-review.js +4 -61
- package/dist/src/graph/create-proposal-review.js.map +1 -1
- package/dist/src/graph/create-relation.d.ts +2 -0
- package/dist/src/graph/create-relation.d.ts.map +1 -1
- package/dist/src/graph/create-relation.js +2 -0
- package/dist/src/graph/create-relation.js.map +1 -1
- package/dist/src/graph/create-type.d.ts +2 -0
- package/dist/src/graph/create-type.d.ts.map +1 -1
- package/dist/src/graph/create-type.js +2 -0
- package/dist/src/graph/create-type.js.map +1 -1
- package/dist/src/graph/delete-entity.d.ts +2 -14
- package/dist/src/graph/delete-entity.d.ts.map +1 -1
- package/dist/src/graph/delete-entity.js +5 -78
- package/dist/src/graph/delete-entity.js.map +1 -1
- package/dist/src/graph/delete-entity.test.js +7 -0
- package/dist/src/graph/delete-entity.test.js.map +1 -1
- package/dist/src/graph/delete-relation.d.ts +2 -0
- package/dist/src/graph/delete-relation.d.ts.map +1 -1
- package/dist/src/graph/delete-relation.js +2 -0
- package/dist/src/graph/delete-relation.js.map +1 -1
- package/dist/src/graph/entity-vote.d.ts +37 -0
- package/dist/src/graph/entity-vote.d.ts.map +1 -0
- package/dist/src/graph/entity-vote.js +44 -0
- package/dist/src/graph/entity-vote.js.map +1 -0
- package/dist/src/graph/entity-vote.test.d.ts +2 -0
- package/dist/src/graph/entity-vote.test.d.ts.map +1 -0
- package/dist/src/graph/entity-vote.test.js +123 -0
- package/dist/src/graph/entity-vote.test.js.map +1 -0
- package/dist/src/graph/index.d.ts +1 -0
- package/dist/src/graph/index.d.ts.map +1 -1
- package/dist/src/graph/index.js +1 -0
- package/dist/src/graph/index.js.map +1 -1
- package/dist/src/graph/update-comment.d.ts +3 -0
- package/dist/src/graph/update-comment.d.ts.map +1 -1
- package/dist/src/graph/update-comment.js +3 -0
- package/dist/src/graph/update-comment.js.map +1 -1
- package/dist/src/graph/update-entity.d.ts +2 -0
- package/dist/src/graph/update-entity.d.ts.map +1 -1
- package/dist/src/graph/update-entity.js +2 -0
- package/dist/src/graph/update-entity.js.map +1 -1
- package/dist/src/graph/update-proposal-review.d.ts +2 -27
- package/dist/src/graph/update-proposal-review.d.ts.map +1 -1
- package/dist/src/graph/update-proposal-review.js +4 -50
- package/dist/src/graph/update-proposal-review.js.map +1 -1
- package/dist/src/graph/update-relation.d.ts +2 -0
- package/dist/src/graph/update-relation.d.ts.map +1 -1
- package/dist/src/graph/update-relation.js +2 -0
- package/dist/src/graph/update-relation.js.map +1 -1
- package/dist/src/ipfs-core.d.ts +42 -0
- package/dist/src/ipfs-core.d.ts.map +1 -0
- package/dist/src/ipfs-core.js +165 -0
- package/dist/src/ipfs-core.js.map +1 -0
- package/dist/src/ipfs-core.test.d.ts +2 -0
- package/dist/src/ipfs-core.test.d.ts.map +1 -0
- package/dist/src/ipfs-core.test.js +56 -0
- package/dist/src/ipfs-core.test.js.map +1 -0
- package/dist/src/ipfs.d.ts +7 -52
- package/dist/src/ipfs.d.ts.map +1 -1
- package/dist/src/ipfs.js +27 -170
- package/dist/src/ipfs.js.map +1 -1
- package/dist/src/networks.d.ts +46 -0
- package/dist/src/networks.d.ts.map +1 -0
- package/dist/src/networks.js +90 -0
- package/dist/src/networks.js.map +1 -0
- package/dist/src/ops/comments.d.ts +80 -0
- package/dist/src/ops/comments.d.ts.map +1 -0
- package/dist/src/ops/comments.js +142 -0
- package/dist/src/ops/comments.js.map +1 -0
- package/dist/src/ops/entities.d.ts +50 -0
- package/dist/src/ops/entities.d.ts.map +1 -0
- package/dist/src/ops/entities.js +51 -0
- package/dist/src/ops/entities.js.map +1 -0
- package/dist/src/ops/images.d.ts +37 -0
- package/dist/src/ops/images.d.ts.map +1 -0
- package/dist/src/ops/images.js +69 -0
- package/dist/src/ops/images.js.map +1 -0
- package/dist/src/ops/index.d.ts +7 -0
- package/dist/src/ops/index.d.ts.map +1 -0
- package/dist/src/ops/index.js +7 -0
- package/dist/src/ops/index.js.map +1 -0
- package/dist/src/ops/index.test.d.ts +2 -0
- package/dist/src/ops/index.test.d.ts.map +1 -0
- package/dist/src/ops/index.test.js +115 -0
- package/dist/src/ops/index.test.js.map +1 -0
- package/dist/src/ops/properties.d.ts +20 -0
- package/dist/src/ops/properties.d.ts.map +1 -0
- package/dist/src/ops/properties.js +20 -0
- package/dist/src/ops/properties.js.map +1 -0
- package/dist/src/ops/proposal-reviews.d.ts +43 -0
- package/dist/src/ops/proposal-reviews.d.ts.map +1 -0
- package/dist/src/ops/proposal-reviews.js +103 -0
- package/dist/src/ops/proposal-reviews.js.map +1 -0
- package/dist/src/ops/relations.d.ts +57 -0
- package/dist/src/ops/relations.d.ts.map +1 -0
- package/dist/src/ops/relations.js +59 -0
- package/dist/src/ops/relations.js.map +1 -0
- package/dist/src/ops/types.d.ts +20 -0
- package/dist/src/ops/types.d.ts.map +1 -0
- package/dist/src/ops/types.js +20 -0
- package/dist/src/ops/types.js.map +1 -0
- package/dist/src/personal-space/constants.d.ts +1 -0
- package/dist/src/personal-space/constants.d.ts.map +1 -1
- package/dist/src/personal-space/constants.js +1 -0
- package/dist/src/personal-space/constants.js.map +1 -1
- package/dist/src/personal-space/create-space.d.ts +1 -18
- package/dist/src/personal-space/create-space.d.ts.map +1 -1
- package/dist/src/personal-space/create-space.js +1 -18
- package/dist/src/personal-space/create-space.js.map +1 -1
- package/dist/src/personal-space/has-space.d.ts +9 -2
- package/dist/src/personal-space/has-space.d.ts.map +1 -1
- package/dist/src/personal-space/has-space.js +9 -13
- package/dist/src/personal-space/has-space.js.map +1 -1
- package/dist/src/personal-space/has-space.test.d.ts +2 -0
- package/dist/src/personal-space/has-space.test.d.ts.map +1 -0
- package/dist/src/personal-space/has-space.test.js +30 -0
- package/dist/src/personal-space/has-space.test.js.map +1 -0
- package/dist/src/personal-space/publish-edit.d.ts +1 -22
- package/dist/src/personal-space/publish-edit.d.ts.map +1 -1
- package/dist/src/personal-space/publish-edit.js +10 -64
- package/dist/src/personal-space/publish-edit.js.map +1 -1
- package/dist/src/smart-wallet.d.ts +2 -4
- package/dist/src/smart-wallet.d.ts.map +1 -1
- package/dist/src/smart-wallet.js +6 -9
- package/dist/src/smart-wallet.js.map +1 -1
- package/dist/src/types.d.ts +25 -0
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js.map +1 -1
- package/package.json +14 -1
package/README.md
CHANGED
|
@@ -14,623 +14,1246 @@ If you're using an AI coding assistant (e.g. Claude Code, Codex, Cursor) to work
|
|
|
14
14
|
|
|
15
15
|
## Overview
|
|
16
16
|
|
|
17
|
-
### Data
|
|
17
|
+
### Data Flow
|
|
18
18
|
|
|
19
19
|
Data in The Graph lives both offchain and onchain. This data is written to IPFS, and the resulting content identifier is then posted onchain before being read by the indexing stack. After the indexer finishes processing the data it's exposed by the API.
|
|
20
|
+
|
|
20
21
|

|
|
21
22
|
|
|
22
23
|
### Spaces
|
|
23
24
|
|
|
24
|
-
On The Graph, knowledge is organized into spaces. Anyone can create a space for a community, project or individual. Spaces are organized onchain into a set of
|
|
25
|
-
|
|
26
|
-
### Relations
|
|
27
|
-
|
|
28
|
-
Relations describe the edges within the graph. Relations are themselves entities that include details about the relationship. For example a Company can have Team Members. Each Team Member relation can have an attribute describing when the person joined the team. This is a model that is commonly called a property graph.
|
|
25
|
+
On The Graph, knowledge is organized into spaces. Anyone can create a space for a community, project or individual. Spaces are organized onchain into a set of smart contracts. These contracts represent the space itself, its data and its governance process.
|
|
29
26
|
|
|
30
27
|
### Entities
|
|
31
28
|
|
|
32
|
-
An entity is a unique identifier representing a person, a place, an idea, a concept, or anything else. Entities are
|
|
29
|
+
An entity is a unique identifier representing a person, a place, an idea, a concept, or anything else. Entities are composed from triples and relations that describe what the entity is. An entity's data can be composed from multiple spaces at once. This property is what enables pluralism within The Graph.
|
|
33
30
|
|
|
34
31
|
[More about entities and knowledge graphs](https://www.geobrowser.io/space/720eb279c64d56735dccd17a2a416ba2/a72654b014fa405bbf1250ba901bc082)
|
|
35
32
|
|
|
36
|
-
###
|
|
33
|
+
### Relations
|
|
34
|
+
|
|
35
|
+
Relations describe the edges within the graph. Relations are themselves entities that include details about the relationship. For example, a Company can have Team Members. Each Team Member relation can have an attribute describing when the person joined the team. This is a property graph model.
|
|
36
|
+
|
|
37
|
+
### Ops And Edits
|
|
37
38
|
|
|
38
39
|
Data in The Graph is stored as an Op (operation). Ops represent a set of changes applied to entities. A change could be setting or deleting a triple or a relation. Both triples and relations are represented as Ops.
|
|
39
40
|
|
|
40
|
-
When writing data, these ops are grouped into a logical set called an
|
|
41
|
+
When writing data, these ops are grouped into a logical set called an edit. An edit has a name, authors, and metadata to represent the set of changes. This edit is encoded into a binary representation before being uploaded to IPFS.
|
|
41
42
|
|
|
42
43
|
[Ops and edits in GRC-20](https://github.com/yanivtal/graph-improvement-proposals/blob/new-ops/grcs/0020-knowledge-graph.md#101-operations-op)
|
|
43
44
|
|
|
44
45
|
## Using
|
|
45
46
|
|
|
46
|
-
|
|
47
|
+
The preferred API has two parts:
|
|
47
48
|
|
|
48
|
-
|
|
49
|
+
- `Ops` builds GRC-20 operations.
|
|
50
|
+
- `createGeoClient(...)` handles configured workflows such as API calls, uploads, RPC reads, and transaction calldata.
|
|
49
51
|
|
|
50
52
|
```ts
|
|
51
|
-
import {
|
|
53
|
+
import { GeoTestnetConfig, Ops, createGeoClient } from "@geoprotocol/geo-sdk";
|
|
54
|
+
|
|
55
|
+
const geo = createGeoClient({ network: GeoTestnetConfig });
|
|
56
|
+
|
|
57
|
+
const { id: entityId, ops } = Ops.entities.create({
|
|
58
|
+
name: "Test Entity",
|
|
59
|
+
description: "Created with the new API",
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const tx = await geo.personalSpaces.publishEdit({
|
|
63
|
+
name: "Create Test Entity",
|
|
64
|
+
spaceId,
|
|
65
|
+
author: spaceId,
|
|
66
|
+
ops,
|
|
67
|
+
});
|
|
52
68
|
|
|
53
|
-
|
|
69
|
+
await walletClient.sendTransaction({ to: tx.to, data: tx.calldata });
|
|
54
70
|
```
|
|
55
71
|
|
|
56
|
-
###
|
|
72
|
+
### Imports
|
|
57
73
|
|
|
58
|
-
|
|
74
|
+
Use the root package when convenience matters:
|
|
59
75
|
|
|
60
76
|
```ts
|
|
61
|
-
import {
|
|
77
|
+
import { GeoTestnetConfig, Ops, createGeoClient } from "@geoprotocol/geo-sdk";
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Subpath exports are available for bundle-sensitive code
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
import { entities, relations } from "@geoprotocol/geo-sdk/ops";
|
|
84
|
+
import { createGeoClient } from "@geoprotocol/geo-sdk/client";
|
|
85
|
+
import {
|
|
86
|
+
GeoTestnetConfig,
|
|
87
|
+
defineGeoNetworkConfig,
|
|
88
|
+
} from "@geoprotocol/geo-sdk/networks";
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Use `Ops` from `@geoprotocol/geo-sdk` for op builders. Configured workflows live behind `createGeoClient`.
|
|
92
|
+
|
|
93
|
+
### Network Configuration
|
|
94
|
+
|
|
95
|
+
Use the built-in testnet config:
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
import { GeoTestnetConfig, createGeoClient } from "@geoprotocol/geo-sdk";
|
|
99
|
+
|
|
100
|
+
const geo = createGeoClient({ network: GeoTestnetConfig });
|
|
101
|
+
|
|
102
|
+
console.log(geo.network.id); // "TESTNET"
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
`network` is required, and the client expects the full config object. Pass `GeoTestnetConfig`, not the string `"TESTNET"`.
|
|
62
106
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
107
|
+
Define a custom deployment:
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
import { createGeoClient, defineGeoNetworkConfig } from "@geoprotocol/geo-sdk";
|
|
111
|
+
|
|
112
|
+
const local = defineGeoNetworkConfig({
|
|
113
|
+
id: "LOCAL",
|
|
114
|
+
name: "Local Geo",
|
|
115
|
+
apiOrigin: "http://localhost:3000",
|
|
116
|
+
chain: {
|
|
117
|
+
id: 31337,
|
|
118
|
+
name: "Anvil",
|
|
119
|
+
rpcUrl: "http://localhost:8545",
|
|
120
|
+
},
|
|
121
|
+
contracts: {
|
|
122
|
+
SPACE_REGISTRY_ADDRESS: "0x0000000000000000000000000000000000000001",
|
|
123
|
+
DAO_SPACE_FACTORY_ADDRESS: "0x0000000000000000000000000000000000000002",
|
|
124
|
+
},
|
|
67
125
|
});
|
|
68
126
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
127
|
+
const geo = createGeoClient({ network: local });
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Pass a fetch implementation when your runtime does not provide `globalThis.fetch`, or when you want test isolation:
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
const geo = createGeoClient({
|
|
134
|
+
network: GeoTestnetConfig,
|
|
135
|
+
fetch: customFetch,
|
|
136
|
+
});
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Ops API
|
|
140
|
+
|
|
141
|
+
Ops functions build operation arrays that can be combined before publishing.
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
import type { Op } from "@geoprotocol/geo-sdk";
|
|
145
|
+
|
|
146
|
+
const ops: Op[] = [];
|
|
147
|
+
ops.push(...entityOps);
|
|
148
|
+
ops.push(...relationOps);
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Properties, Types, And Entities
|
|
152
|
+
|
|
153
|
+
Create a property, a type, and an entity:
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
import { Ops } from "@geoprotocol/geo-sdk";
|
|
157
|
+
|
|
158
|
+
const { id: websitePropertyId, ops: propertyOps } = Ops.properties.create({
|
|
159
|
+
name: "Website",
|
|
160
|
+
dataType: "TEXT",
|
|
73
161
|
});
|
|
74
162
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
// blob: new Blob([fs.readFileSync(path.join(__dirname, 'cover.png'))], { type: 'image/png' });
|
|
163
|
+
const { id: restaurantTypeId, ops: typeOps } = Ops.types.create({
|
|
164
|
+
name: "Restaurant",
|
|
165
|
+
properties: [websitePropertyId],
|
|
79
166
|
});
|
|
80
167
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
types: […listOfTypeIds],
|
|
86
|
-
cover: imageId,
|
|
168
|
+
const { id: restaurantId, ops: entityOps } = Ops.entities.create({
|
|
169
|
+
name: "Yum Yum",
|
|
170
|
+
description: "A restaurant serving fusion cuisine",
|
|
171
|
+
types: [restaurantTypeId],
|
|
87
172
|
values: [
|
|
88
173
|
{
|
|
89
|
-
property:
|
|
90
|
-
type:
|
|
91
|
-
value:
|
|
92
|
-
}
|
|
93
|
-
],
|
|
94
|
-
relations: {
|
|
95
|
-
// relation property
|
|
96
|
-
[relationPropertyId]: {
|
|
97
|
-
toEntity: 'id of the entity',
|
|
98
|
-
id: 'id of the relation', // optional
|
|
99
|
-
position: positionString, // optional
|
|
174
|
+
property: websitePropertyId,
|
|
175
|
+
type: "text",
|
|
176
|
+
value: "https://example.com",
|
|
100
177
|
},
|
|
101
|
-
|
|
178
|
+
],
|
|
102
179
|
});
|
|
103
|
-
```
|
|
104
180
|
|
|
105
|
-
|
|
181
|
+
const ops = [...propertyOps, ...typeOps, ...entityOps];
|
|
182
|
+
```
|
|
106
183
|
|
|
107
|
-
|
|
184
|
+
Update an entity:
|
|
108
185
|
|
|
109
186
|
```ts
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
187
|
+
const { ops } = Ops.entities.update({
|
|
188
|
+
id: restaurantId,
|
|
189
|
+
name: "Yum Yum Kitchen",
|
|
113
190
|
values: [
|
|
114
|
-
// Text value (with optional language)
|
|
115
191
|
{
|
|
116
|
-
property:
|
|
192
|
+
property: websitePropertyId,
|
|
117
193
|
type: "text",
|
|
118
|
-
value: "
|
|
119
|
-
language: Id("dad6e52a5e944e559411cfe3a3c3ea64"), // optional
|
|
194
|
+
value: "https://yum.example",
|
|
120
195
|
},
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
196
|
+
],
|
|
197
|
+
});
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Unset property values:
|
|
201
|
+
|
|
202
|
+
```ts
|
|
203
|
+
const { ops } = Ops.entities.update({
|
|
204
|
+
id: restaurantId,
|
|
205
|
+
unset: [
|
|
206
|
+
{ property: websitePropertyId },
|
|
207
|
+
{ property: descriptionPropertyId, language: "all" },
|
|
208
|
+
],
|
|
209
|
+
});
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Create relation properties:
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
const { id: likesPropertyId, ops } = Ops.properties.create({
|
|
216
|
+
name: "Likes",
|
|
217
|
+
dataType: "RELATION",
|
|
218
|
+
relationValueTypes: [restaurantTypeId],
|
|
219
|
+
});
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Typed Values
|
|
223
|
+
|
|
224
|
+
Entity values are typed objects. The `type` field determines the value shape:
|
|
225
|
+
|
|
226
|
+
```ts
|
|
227
|
+
import { Ops } from "@geoprotocol/geo-sdk";
|
|
228
|
+
|
|
229
|
+
const { ops } = Ops.entities.create({
|
|
230
|
+
name: "Cafe visit",
|
|
231
|
+
values: [
|
|
232
|
+
{ property: namePropertyId, type: "text", value: "Morning Coffee" },
|
|
233
|
+
{ property: openPropertyId, type: "boolean", value: true },
|
|
234
|
+
{ property: visitsPropertyId, type: "integer", value: 42 },
|
|
235
|
+
{ property: ratingPropertyId, type: "float", value: 4.8 },
|
|
236
|
+
{ property: openedDatePropertyId, type: "date", value: "2024-01-15" },
|
|
237
|
+
{ property: opensAtPropertyId, type: "time", value: "08:30:00Z" },
|
|
129
238
|
{
|
|
130
|
-
property:
|
|
131
|
-
type: "
|
|
132
|
-
value:
|
|
133
|
-
unit: Id("016c9b1cd8a84e4d9e844e40878bb235"), // optional
|
|
239
|
+
property: reviewedAtPropertyId,
|
|
240
|
+
type: "datetime",
|
|
241
|
+
value: "2024-01-15T14:30:00Z",
|
|
134
242
|
},
|
|
135
|
-
// Boolean value
|
|
136
243
|
{
|
|
137
|
-
property:
|
|
138
|
-
type: "
|
|
139
|
-
value:
|
|
244
|
+
property: schedulePropertyId,
|
|
245
|
+
type: "schedule",
|
|
246
|
+
value: "FREQ=WEEKLY;BYDAY=MO,WE,FR",
|
|
140
247
|
},
|
|
141
|
-
// Point value (with optional altitude)
|
|
142
248
|
{
|
|
143
|
-
property:
|
|
249
|
+
property: locationPropertyId,
|
|
144
250
|
type: "point",
|
|
145
251
|
lon: -122.4194,
|
|
146
252
|
lat: 37.7749,
|
|
147
|
-
alt: 10.5, // optional
|
|
148
253
|
},
|
|
149
|
-
// Date value (ISO 8601 format: YYYY-MM-DD)
|
|
150
254
|
{
|
|
151
|
-
property:
|
|
152
|
-
type: "
|
|
153
|
-
value:
|
|
154
|
-
},
|
|
155
|
-
// Time value (ISO 8601 format with timezone)
|
|
156
|
-
{
|
|
157
|
-
property: someTimePropertyId,
|
|
158
|
-
type: "time",
|
|
159
|
-
value: "14:30:00Z",
|
|
160
|
-
},
|
|
161
|
-
// Datetime value (ISO 8601 combined format)
|
|
162
|
-
{
|
|
163
|
-
property: someDatetimePropertyId,
|
|
164
|
-
type: "datetime",
|
|
165
|
-
value: "2024-01-15T14:30:00Z",
|
|
166
|
-
},
|
|
167
|
-
// Schedule value (iCalendar RRULE format)
|
|
168
|
-
{
|
|
169
|
-
property: someSchedulePropertyId,
|
|
170
|
-
type: "schedule",
|
|
171
|
-
value: "FREQ=WEEKLY;BYDAY=MO,WE,FR",
|
|
255
|
+
property: bytesPropertyId,
|
|
256
|
+
type: "bytes",
|
|
257
|
+
value: new Uint8Array([1, 2, 3]),
|
|
172
258
|
},
|
|
173
259
|
],
|
|
174
260
|
});
|
|
175
261
|
```
|
|
176
262
|
|
|
177
|
-
|
|
263
|
+
### Relations
|
|
178
264
|
|
|
179
|
-
|
|
180
|
-
import { Graph, type Op } from "@geoprotocol/geo-sdk";
|
|
265
|
+
Create, update, and delete relation entities:
|
|
181
266
|
|
|
182
|
-
|
|
267
|
+
```ts
|
|
268
|
+
import { Ops, Position } from "@geoprotocol/geo-sdk";
|
|
183
269
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
270
|
+
const { id: relationId, ops: createRelationOps } = Ops.relations.create({
|
|
271
|
+
fromEntity: personId,
|
|
272
|
+
toEntity: restaurantId,
|
|
273
|
+
type: likesPropertyId,
|
|
274
|
+
position: Position.generate(),
|
|
188
275
|
});
|
|
189
|
-
ops.push(...createAgePropertyOps);
|
|
190
276
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
name: "Likes",
|
|
196
|
-
});
|
|
197
|
-
ops.push(...createLikesPropertyOps);
|
|
277
|
+
const { ops: updateRelationOps } = Ops.relations.update({
|
|
278
|
+
id: relationId,
|
|
279
|
+
position: Position.generateBetween(previousPosition, nextPosition),
|
|
280
|
+
});
|
|
198
281
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
name: "Person",
|
|
202
|
-
cover: personCoverId,
|
|
203
|
-
properties: [agePropertyId, likesPropertyId],
|
|
282
|
+
const { ops: deleteRelationOps } = Ops.relations.delete({
|
|
283
|
+
id: relationId,
|
|
204
284
|
});
|
|
205
|
-
|
|
285
|
+
```
|
|
206
286
|
|
|
207
|
-
|
|
208
|
-
const { id: restaurantCoverId, ops: createRestaurantCoverOps } =
|
|
209
|
-
await Graph.createImage({
|
|
210
|
-
url: "https://example.com/image.png",
|
|
211
|
-
});
|
|
212
|
-
ops.push(...createRestaurantCoverOps);
|
|
287
|
+
Relations can also carry their own entity values:
|
|
213
288
|
|
|
214
|
-
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
values: [
|
|
289
|
+
```ts
|
|
290
|
+
const { ops } = Ops.relations.create({
|
|
291
|
+
fromEntity: personId,
|
|
292
|
+
toEntity: companyId,
|
|
293
|
+
type: teamMemberPropertyId,
|
|
294
|
+
entityName: "Team member",
|
|
295
|
+
entityValues: [
|
|
222
296
|
{
|
|
223
|
-
property:
|
|
224
|
-
type: "
|
|
225
|
-
value: "
|
|
297
|
+
property: joinedDatePropertyId,
|
|
298
|
+
type: "date",
|
|
299
|
+
value: "2024-05-01",
|
|
226
300
|
},
|
|
227
301
|
],
|
|
228
302
|
});
|
|
229
|
-
|
|
303
|
+
```
|
|
230
304
|
|
|
231
|
-
|
|
232
|
-
const { id: personCoverId, ops: createPersonCoverOps } =
|
|
233
|
-
await Graph.createImage({
|
|
234
|
-
url: "https://example.com/avatar.png",
|
|
235
|
-
});
|
|
236
|
-
ops.push(...createPersonCoverOps);
|
|
305
|
+
### Images
|
|
237
306
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
307
|
+
Image creation goes through `geo.images.create(...)`. The configured client uploads the image, detects dimensions when possible, and returns the image entity ops.
|
|
308
|
+
|
|
309
|
+
### Comments
|
|
310
|
+
|
|
311
|
+
Create a comment on an entity:
|
|
312
|
+
|
|
313
|
+
```ts
|
|
314
|
+
import { Ops } from "@geoprotocol/geo-sdk";
|
|
315
|
+
|
|
316
|
+
const { id: commentId, ops } = Ops.comments.create({
|
|
317
|
+
content: "Looks good to me.",
|
|
318
|
+
replyTo: {
|
|
319
|
+
entityId,
|
|
320
|
+
spaceId,
|
|
321
|
+
},
|
|
322
|
+
});
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
Create a nested comment when you already have reply-chain context:
|
|
326
|
+
|
|
327
|
+
```ts
|
|
328
|
+
const { ops } = Ops.comments.create({
|
|
329
|
+
content: "Replying to the parent comment.",
|
|
330
|
+
replyTo: {
|
|
331
|
+
entityId: parentCommentId,
|
|
332
|
+
spaceId,
|
|
333
|
+
},
|
|
334
|
+
replyToRelations: [
|
|
244
335
|
{
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
336
|
+
entityId: rootEntityId,
|
|
337
|
+
spaceId,
|
|
338
|
+
position: "a0",
|
|
248
339
|
},
|
|
249
340
|
],
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
341
|
+
});
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
Update a comment:
|
|
345
|
+
|
|
346
|
+
```ts
|
|
347
|
+
const { ops } = Ops.comments.update({
|
|
348
|
+
id: commentId,
|
|
349
|
+
content: "Updated comment text.",
|
|
350
|
+
resolved: true,
|
|
351
|
+
});
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
Use `geo.comments.create(...)` when reply-chain context should be fetched from the configured API.
|
|
355
|
+
|
|
356
|
+
### Proposal Reviews
|
|
357
|
+
|
|
358
|
+
Create and update proposal review ops:
|
|
359
|
+
|
|
360
|
+
```ts
|
|
361
|
+
import { Ops } from "@geoprotocol/geo-sdk";
|
|
362
|
+
|
|
363
|
+
const { id: reviewId, ops: createReviewOps } = Ops.proposalReviews.create({
|
|
364
|
+
proposal: {
|
|
365
|
+
id: proposalId,
|
|
366
|
+
name: "Improve restaurant data",
|
|
254
367
|
},
|
|
368
|
+
pass: true,
|
|
369
|
+
content: "The proposal is complete and accurate.",
|
|
370
|
+
completeness: 1,
|
|
371
|
+
accuracy: 0.8,
|
|
372
|
+
skill: 0.8,
|
|
373
|
+
effort: 0.6,
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
const { ops: updateReviewOps } = Ops.proposalReviews.update({
|
|
377
|
+
proposalReviewId: reviewId,
|
|
378
|
+
pass: false,
|
|
379
|
+
content: "Needs more sources.",
|
|
255
380
|
});
|
|
256
|
-
ops.push(...createPersonOps);
|
|
257
381
|
```
|
|
258
382
|
|
|
259
|
-
|
|
383
|
+
## Client API
|
|
260
384
|
|
|
261
|
-
|
|
385
|
+
Create a configured client once and use it for workflows that need API origins, uploads, RPC URLs, or contract addresses.
|
|
262
386
|
|
|
263
387
|
```ts
|
|
264
|
-
import {
|
|
388
|
+
import { GeoTestnetConfig, Ops, createGeoClient } from "@geoprotocol/geo-sdk";
|
|
265
389
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
390
|
+
const geo = createGeoClient({ network: GeoTestnetConfig });
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
### `geo.api`
|
|
394
|
+
|
|
395
|
+
Use `geo.api.graphql(...)` for low-level GraphQL requests:
|
|
396
|
+
|
|
397
|
+
```ts
|
|
398
|
+
const response = await geo.api.graphql<{
|
|
399
|
+
entity: { id: string; name: string | null } | null;
|
|
400
|
+
}>(`
|
|
401
|
+
query {
|
|
402
|
+
entity(id: "3af3e22d21694a078681516710b7ecf1") {
|
|
403
|
+
id
|
|
404
|
+
name
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
`);
|
|
408
|
+
|
|
409
|
+
if (response.errors) {
|
|
410
|
+
console.error(response.errors);
|
|
411
|
+
}
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
Use `geo.api.getEditCalldata(...)` when calldata must come from the API:
|
|
415
|
+
|
|
416
|
+
```ts
|
|
417
|
+
const { to, data } = await geo.api.getEditCalldata({
|
|
418
|
+
spaceId,
|
|
419
|
+
cid: "ipfs://bafkreihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku",
|
|
278
420
|
});
|
|
279
421
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
422
|
+
await walletClient.sendTransaction({ to, data });
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
For personal-space edits, prefer `geo.personalSpaces.publishEdit(...)`.
|
|
426
|
+
|
|
427
|
+
### `geo.images`
|
|
428
|
+
|
|
429
|
+
Upload an image and build image entity ops in one call:
|
|
430
|
+
|
|
431
|
+
```ts
|
|
432
|
+
const imageFromUrl = await geo.images.create({
|
|
433
|
+
url: "https://example.com/cover.png",
|
|
434
|
+
name: "Cover image",
|
|
435
|
+
description: "Image used as the space cover.",
|
|
287
436
|
});
|
|
437
|
+
|
|
438
|
+
console.log(imageFromUrl.id);
|
|
439
|
+
console.log(imageFromUrl.cid);
|
|
440
|
+
console.log(imageFromUrl.ops);
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
You can also pass a `Blob` directly:
|
|
444
|
+
|
|
445
|
+
```ts
|
|
446
|
+
const file = new Blob([imageBytes], { type: "image/png" });
|
|
447
|
+
|
|
448
|
+
const imageFromBlob = await geo.images.create({
|
|
449
|
+
blob: file,
|
|
450
|
+
name: "Cover image",
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
console.log(imageFromBlob.cid);
|
|
288
454
|
```
|
|
289
455
|
|
|
290
|
-
###
|
|
456
|
+
### `geo.storage`
|
|
291
457
|
|
|
292
|
-
|
|
458
|
+
Use `geo.images.create(...)` when you want to upload an image and create image entity ops in one call. Use `geo.storage.uploadImage(...)` only when you need the raw uploaded CID and detected dimensions without graph ops:
|
|
293
459
|
|
|
294
460
|
```ts
|
|
295
|
-
|
|
461
|
+
const uploaded = await geo.storage.uploadImage({
|
|
462
|
+
url: "https://example.com/photo.png",
|
|
463
|
+
});
|
|
296
464
|
|
|
297
|
-
|
|
465
|
+
console.log(uploaded.cid);
|
|
466
|
+
console.log(uploaded.dimensions);
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
Upload CSV content:
|
|
470
|
+
|
|
471
|
+
```ts
|
|
472
|
+
const cid = await geo.storage.uploadCSV(`name,score
|
|
473
|
+
Alice,10
|
|
474
|
+
Bob,8`);
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
### `geo.entities`
|
|
478
|
+
|
|
479
|
+
Delete an entity from a space:
|
|
480
|
+
|
|
481
|
+
```ts
|
|
482
|
+
const { ops } = await geo.entities.delete({
|
|
298
483
|
id: entityId,
|
|
299
|
-
spaceId
|
|
300
|
-
network: "TESTNET", // optional, defaults to "TESTNET"
|
|
484
|
+
spaceId,
|
|
301
485
|
});
|
|
302
486
|
```
|
|
303
487
|
|
|
304
|
-
|
|
488
|
+
Entity deletion queries the configured API for current values and relations in the target space, then builds ops to unset those values and delete those relations. The entity transitions to an empty state in that space; it is not globally destroyed.
|
|
305
489
|
|
|
306
|
-
###
|
|
490
|
+
### `geo.comments`
|
|
307
491
|
|
|
308
|
-
|
|
492
|
+
Create a comment while preserving reply-to chains:
|
|
309
493
|
|
|
310
494
|
```ts
|
|
311
|
-
|
|
495
|
+
const comment = await geo.comments.create({
|
|
496
|
+
content: "This should be easier to find.",
|
|
497
|
+
replyTo: {
|
|
498
|
+
entityId,
|
|
499
|
+
spaceId,
|
|
500
|
+
},
|
|
501
|
+
});
|
|
502
|
+
```
|
|
312
503
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
504
|
+
Reply to another comment:
|
|
505
|
+
|
|
506
|
+
```ts
|
|
507
|
+
const reply = await geo.comments.create({
|
|
508
|
+
content: "Following up here.",
|
|
509
|
+
replyTo: {
|
|
510
|
+
entityId: comment.id,
|
|
511
|
+
spaceId,
|
|
512
|
+
},
|
|
319
513
|
});
|
|
514
|
+
```
|
|
320
515
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
516
|
+
Update a comment:
|
|
517
|
+
|
|
518
|
+
```ts
|
|
519
|
+
const { ops } = geo.comments.update({
|
|
520
|
+
id: comment.id,
|
|
521
|
+
content: "Updated comment text.",
|
|
522
|
+
resolved: true,
|
|
325
523
|
});
|
|
524
|
+
```
|
|
326
525
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
526
|
+
### `geo.personalSpaces`
|
|
527
|
+
|
|
528
|
+
Check whether an address already has a personal space:
|
|
529
|
+
|
|
530
|
+
```ts
|
|
531
|
+
const hasExistingSpace = await geo.personalSpaces.hasSpace({
|
|
532
|
+
address: account.address,
|
|
330
533
|
});
|
|
331
534
|
```
|
|
332
535
|
|
|
333
|
-
|
|
536
|
+
Create a personal space:
|
|
537
|
+
|
|
538
|
+
```ts
|
|
539
|
+
const createSpace = geo.personalSpaces.create({
|
|
540
|
+
name: "Alice",
|
|
541
|
+
accountAddress: account.address,
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
await walletClient.sendTransaction({
|
|
545
|
+
to: createSpace.to,
|
|
546
|
+
data: createSpace.calldata,
|
|
547
|
+
});
|
|
548
|
+
```
|
|
334
549
|
|
|
335
|
-
|
|
550
|
+
`geo.personalSpaces.create(...)` also returns initial profile ops. After the create-space transaction is mined and you know the new `spaceId`, publish those ops into the space and immediately set the generated space entity as the space topic:
|
|
336
551
|
|
|
337
552
|
```ts
|
|
338
|
-
|
|
553
|
+
const profileTx = await geo.personalSpaces.publishEdit({
|
|
554
|
+
name: "Create personal space profile",
|
|
555
|
+
spaceId,
|
|
556
|
+
author: spaceId,
|
|
557
|
+
ops: createSpace.ops,
|
|
558
|
+
});
|
|
339
559
|
|
|
340
|
-
|
|
341
|
-
|
|
560
|
+
await walletClient.sendTransaction({
|
|
561
|
+
to: profileTx.to,
|
|
562
|
+
data: profileTx.calldata,
|
|
563
|
+
});
|
|
342
564
|
|
|
343
|
-
|
|
344
|
-
|
|
565
|
+
const topicTx = geo.personalSpaces.setTopic({
|
|
566
|
+
spaceId,
|
|
567
|
+
topicId: createSpace.spaceEntityId,
|
|
568
|
+
});
|
|
345
569
|
|
|
346
|
-
|
|
347
|
-
|
|
570
|
+
await walletClient.sendTransaction({
|
|
571
|
+
to: topicTx.to,
|
|
572
|
+
data: topicTx.calldata,
|
|
573
|
+
});
|
|
574
|
+
```
|
|
348
575
|
|
|
349
|
-
|
|
350
|
-
const last = Position.generateBetween(pos1, null);
|
|
576
|
+
Publish any edit to a personal space:
|
|
351
577
|
|
|
352
|
-
|
|
353
|
-
const
|
|
578
|
+
```ts
|
|
579
|
+
const { ops } = Ops.entities.create({
|
|
580
|
+
name: "Test Entity",
|
|
581
|
+
});
|
|
354
582
|
|
|
355
|
-
|
|
356
|
-
|
|
583
|
+
const tx = await geo.personalSpaces.publishEdit({
|
|
584
|
+
name: "Create Test Entity",
|
|
585
|
+
spaceId,
|
|
586
|
+
author: spaceId,
|
|
587
|
+
ops,
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
await walletClient.sendTransaction({
|
|
591
|
+
to: tx.to,
|
|
592
|
+
data: tx.calldata,
|
|
593
|
+
});
|
|
357
594
|
```
|
|
358
595
|
|
|
359
|
-
|
|
596
|
+
The `author` field is the author's personal space ID, not a wallet address.
|
|
597
|
+
|
|
598
|
+
### `geo.daoSpaces`
|
|
360
599
|
|
|
361
|
-
|
|
600
|
+
Create a DAO space:
|
|
601
|
+
|
|
602
|
+
```ts
|
|
603
|
+
const tx = await geo.daoSpaces.create({
|
|
604
|
+
name: "Research DAO",
|
|
605
|
+
author: authorSpaceId,
|
|
606
|
+
initialEditorSpaceIds: [authorSpaceId],
|
|
607
|
+
initialMemberSpaceIds: [authorSpaceId],
|
|
608
|
+
votingSettings: {
|
|
609
|
+
slowPathPercentageThreshold: 50,
|
|
610
|
+
fastPathFlatThreshold: 1,
|
|
611
|
+
quorum: 1,
|
|
612
|
+
durationInDays: 3,
|
|
613
|
+
},
|
|
614
|
+
});
|
|
362
615
|
|
|
363
|
-
|
|
616
|
+
await walletClient.sendTransaction({
|
|
617
|
+
to: tx.to,
|
|
618
|
+
data: tx.calldata,
|
|
619
|
+
});
|
|
620
|
+
```
|
|
364
621
|
|
|
365
|
-
|
|
622
|
+
Include extra initial ops in the DAO creation edit:
|
|
366
623
|
|
|
367
624
|
```ts
|
|
368
|
-
|
|
625
|
+
const { ops } = Ops.entities.create({
|
|
626
|
+
name: "DAO Welcome Page",
|
|
627
|
+
});
|
|
369
628
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
629
|
+
const tx = await geo.daoSpaces.create({
|
|
630
|
+
name: "Research DAO",
|
|
631
|
+
author: authorSpaceId,
|
|
632
|
+
initialEditorSpaceIds: [authorSpaceId],
|
|
633
|
+
votingSettings,
|
|
634
|
+
ops,
|
|
376
635
|
});
|
|
377
636
|
```
|
|
378
637
|
|
|
379
|
-
|
|
638
|
+
Propose an edit to a DAO space:
|
|
380
639
|
|
|
381
|
-
|
|
640
|
+
```ts
|
|
641
|
+
const { ops } = Ops.entities.update({
|
|
642
|
+
id: entityId,
|
|
643
|
+
name: "Updated entity name",
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
const proposal = await geo.daoSpaces.proposeEdit({
|
|
647
|
+
name: "Update entity name",
|
|
648
|
+
ops,
|
|
649
|
+
author: authorSpaceId,
|
|
650
|
+
daoSpaceAddress,
|
|
651
|
+
callerSpaceId: authorSpaceId,
|
|
652
|
+
daoSpaceId,
|
|
653
|
+
votingMode: "FAST",
|
|
654
|
+
});
|
|
382
655
|
|
|
383
|
-
|
|
656
|
+
await walletClient.sendTransaction({
|
|
657
|
+
to: proposal.to,
|
|
658
|
+
data: proposal.calldata,
|
|
659
|
+
});
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
Manage DAO members and editors:
|
|
384
663
|
|
|
385
664
|
```ts
|
|
386
|
-
|
|
665
|
+
const addMember = geo.daoSpaces.proposeAddMember({
|
|
666
|
+
authorSpaceId,
|
|
667
|
+
spaceId: daoSpaceId,
|
|
668
|
+
daoSpaceAddress,
|
|
669
|
+
newMemberSpaceId: memberSpaceId,
|
|
670
|
+
votingMode: "FAST",
|
|
671
|
+
});
|
|
387
672
|
|
|
388
|
-
const
|
|
389
|
-
|
|
673
|
+
const removeMember = geo.daoSpaces.proposeRemoveMember({
|
|
674
|
+
authorSpaceId,
|
|
675
|
+
spaceId: daoSpaceId,
|
|
676
|
+
daoSpaceAddress,
|
|
677
|
+
memberToRemoveSpaceId: memberSpaceId,
|
|
390
678
|
});
|
|
391
679
|
|
|
392
|
-
|
|
393
|
-
|
|
680
|
+
const addEditor = geo.daoSpaces.proposeAddEditor({
|
|
681
|
+
authorSpaceId,
|
|
682
|
+
spaceId: daoSpaceId,
|
|
683
|
+
daoSpaceAddress,
|
|
684
|
+
newEditorSpaceId: editorSpaceId,
|
|
685
|
+
});
|
|
394
686
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
687
|
+
const removeEditor = geo.daoSpaces.proposeRemoveEditor({
|
|
688
|
+
authorSpaceId,
|
|
689
|
+
spaceId: daoSpaceId,
|
|
690
|
+
daoSpaceAddress,
|
|
691
|
+
editorToRemoveSpaceId: editorSpaceId,
|
|
400
692
|
});
|
|
401
693
|
```
|
|
402
694
|
|
|
403
|
-
|
|
695
|
+
Editor changes only support `SLOW` voting. Member changes can use the default or an explicit `votingMode`.
|
|
404
696
|
|
|
405
|
-
|
|
406
|
-
import { personalSpace } from "@geoprotocol/geo-sdk";
|
|
697
|
+
Request membership in a DAO space:
|
|
407
698
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
author: spaceId, // your personal space ID (same as spaceId for personal spaces)
|
|
413
|
-
network: "TESTNET",
|
|
699
|
+
```ts
|
|
700
|
+
const request = geo.daoSpaces.proposeRequestMembership({
|
|
701
|
+
authorSpaceId: requesterSpaceId,
|
|
702
|
+
spaceId: daoSpaceId,
|
|
414
703
|
});
|
|
415
704
|
|
|
416
|
-
|
|
705
|
+
await walletClient.sendTransaction({
|
|
706
|
+
to: request.to,
|
|
707
|
+
data: request.calldata,
|
|
708
|
+
});
|
|
417
709
|
```
|
|
418
710
|
|
|
419
|
-
|
|
711
|
+
DAO proposal IDs and DAO space IDs are bytes16 hex strings, usually `0x` plus 32 hex characters.
|
|
712
|
+
|
|
713
|
+
### `geo.daoSpaces.proposals`
|
|
420
714
|
|
|
421
|
-
|
|
715
|
+
Use low-level proposal helpers when you want to assemble proposal actions yourself.
|
|
422
716
|
|
|
423
|
-
|
|
717
|
+
```ts
|
|
718
|
+
const actions = [
|
|
719
|
+
geo.daoSpaces.proposals.actions.addMember(daoSpaceAddress, memberSpaceId),
|
|
720
|
+
geo.daoSpaces.proposals.actions.updateVotingSettings(daoSpaceAddress, {
|
|
721
|
+
slowPathPercentageThreshold: 60,
|
|
722
|
+
fastPathFlatThreshold: 2,
|
|
723
|
+
quorum: 3,
|
|
724
|
+
durationInDays: 5,
|
|
725
|
+
}),
|
|
726
|
+
];
|
|
727
|
+
|
|
728
|
+
const proposal = geo.daoSpaces.proposals.create({
|
|
729
|
+
fromSpaceId: authorSpaceId,
|
|
730
|
+
daoSpaceId,
|
|
731
|
+
votingMode: "SLOW",
|
|
732
|
+
actions,
|
|
733
|
+
});
|
|
734
|
+
```
|
|
424
735
|
|
|
425
|
-
|
|
736
|
+
Available proposal actions:
|
|
426
737
|
|
|
427
738
|
```ts
|
|
428
|
-
|
|
739
|
+
geo.daoSpaces.proposals.actions.publishEdit(daoSpaceAddress, cid);
|
|
740
|
+
geo.daoSpaces.proposals.actions.addMember(daoSpaceAddress, memberSpaceId);
|
|
741
|
+
geo.daoSpaces.proposals.actions.removeMember(daoSpaceAddress, memberSpaceId);
|
|
742
|
+
geo.daoSpaces.proposals.actions.addEditor(daoSpaceAddress, editorSpaceId);
|
|
743
|
+
geo.daoSpaces.proposals.actions.removeEditor(daoSpaceAddress, editorSpaceId);
|
|
744
|
+
geo.daoSpaces.proposals.actions.updateVotingSettings(
|
|
745
|
+
daoSpaceAddress,
|
|
746
|
+
votingSettings,
|
|
747
|
+
);
|
|
748
|
+
```
|
|
429
749
|
|
|
430
|
-
|
|
750
|
+
Vote on a proposal:
|
|
431
751
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
752
|
+
```ts
|
|
753
|
+
const vote = geo.daoSpaces.proposals.vote({
|
|
754
|
+
authorSpaceId,
|
|
755
|
+
spaceId: daoSpaceId,
|
|
756
|
+
proposalId,
|
|
757
|
+
vote: "YES",
|
|
758
|
+
});
|
|
759
|
+
|
|
760
|
+
await walletClient.sendTransaction({
|
|
761
|
+
to: vote.to,
|
|
762
|
+
data: vote.calldata,
|
|
763
|
+
});
|
|
764
|
+
```
|
|
765
|
+
|
|
766
|
+
Execute a passed proposal:
|
|
767
|
+
|
|
768
|
+
```ts
|
|
769
|
+
const execute = geo.daoSpaces.proposals.execute({
|
|
770
|
+
authorSpaceId,
|
|
771
|
+
spaceId: daoSpaceId,
|
|
772
|
+
proposalId,
|
|
440
773
|
});
|
|
441
774
|
|
|
442
|
-
await walletClient.sendTransaction({
|
|
775
|
+
await walletClient.sendTransaction({
|
|
776
|
+
to: execute.to,
|
|
777
|
+
data: execute.calldata,
|
|
778
|
+
});
|
|
443
779
|
```
|
|
444
780
|
|
|
445
|
-
|
|
781
|
+
### `geo.entityVotes`
|
|
446
782
|
|
|
447
|
-
|
|
783
|
+
Upvote, downvote, or withdraw a vote on an entity:
|
|
448
784
|
|
|
449
785
|
```ts
|
|
450
|
-
|
|
786
|
+
const upvote = geo.entityVotes.upvote({
|
|
787
|
+
authorSpaceId,
|
|
788
|
+
spaceId,
|
|
789
|
+
entityId,
|
|
790
|
+
});
|
|
451
791
|
|
|
452
|
-
const
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
792
|
+
const downvote = geo.entityVotes.downvote({
|
|
793
|
+
authorSpaceId,
|
|
794
|
+
spaceId,
|
|
795
|
+
entityId,
|
|
456
796
|
});
|
|
457
797
|
|
|
458
|
-
|
|
798
|
+
const withdraw = geo.entityVotes.withdraw({
|
|
799
|
+
authorSpaceId,
|
|
800
|
+
spaceId,
|
|
801
|
+
entityId,
|
|
802
|
+
});
|
|
459
803
|
```
|
|
460
804
|
|
|
461
|
-
|
|
805
|
+
Submit any returned transaction with your wallet client:
|
|
462
806
|
|
|
463
|
-
|
|
807
|
+
```ts
|
|
808
|
+
await walletClient.sendTransaction({
|
|
809
|
+
to: upvote.to,
|
|
810
|
+
data: upvote.calldata,
|
|
811
|
+
});
|
|
812
|
+
```
|
|
813
|
+
|
|
814
|
+
## Full Publishing Flow With A Smart Account
|
|
464
815
|
|
|
465
|
-
|
|
816
|
+
This example publishes an edit to an existing personal space using a Geo smart account.
|
|
466
817
|
|
|
467
818
|
```ts
|
|
468
|
-
import { createPublicClient, type Hex
|
|
819
|
+
import { createPublicClient, http, type Hex } from "viem";
|
|
469
820
|
import {
|
|
470
|
-
|
|
471
|
-
|
|
821
|
+
GeoTestnetConfig,
|
|
822
|
+
Ops,
|
|
823
|
+
createGeoClient,
|
|
472
824
|
getSmartAccountWalletClient,
|
|
473
|
-
TESTNET_RPC_URL,
|
|
474
825
|
} from "@geoprotocol/geo-sdk";
|
|
475
826
|
import { SpaceRegistryAbi } from "@geoprotocol/geo-sdk/abis";
|
|
476
|
-
import { TESTNET } from "@geoprotocol/geo-sdk/contracts";
|
|
477
827
|
|
|
478
|
-
// IMPORTANT: Be careful with your private key. Don't commit it to version control.
|
|
479
|
-
// You can get your private key using https://www.geobrowser.io/export-wallet
|
|
480
828
|
const privateKey = `0x${privateKeyFromGeoWallet}` as `0x${string}`;
|
|
481
|
-
|
|
482
|
-
// Get smart account wallet client (Safe + Pimlico paymaster)
|
|
829
|
+
const geo = createGeoClient({ network: GeoTestnetConfig });
|
|
483
830
|
const smartAccount = await getSmartAccountWalletClient({ privateKey });
|
|
484
|
-
const
|
|
831
|
+
const accountAddress = smartAccount.account.address;
|
|
485
832
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
address: smartAccountAddress,
|
|
833
|
+
const hasSpace = await geo.personalSpaces.hasSpace({
|
|
834
|
+
address: accountAddress,
|
|
489
835
|
});
|
|
490
|
-
|
|
491
|
-
|
|
836
|
+
|
|
837
|
+
if (!hasSpace) {
|
|
838
|
+
throw new Error("No personal space found for this account.");
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
const spaceRegistryAddress = GeoTestnetConfig.contracts?.SPACE_REGISTRY_ADDRESS;
|
|
842
|
+
const rpcUrl = GeoTestnetConfig.chain?.rpcUrl;
|
|
843
|
+
if (!spaceRegistryAddress || !rpcUrl) {
|
|
844
|
+
throw new Error("GeoTestnetConfig is missing registry or RPC configuration.");
|
|
492
845
|
}
|
|
493
846
|
|
|
494
847
|
const publicClient = createPublicClient({
|
|
495
|
-
transport: http(
|
|
848
|
+
transport: http(rpcUrl),
|
|
496
849
|
});
|
|
497
850
|
|
|
498
|
-
// Look up the space ID for this smart account address
|
|
499
851
|
const spaceIdHex = (await publicClient.readContract({
|
|
500
|
-
address:
|
|
852
|
+
address: spaceRegistryAddress,
|
|
501
853
|
abi: SpaceRegistryAbi,
|
|
502
854
|
functionName: "addressToSpaceId",
|
|
503
|
-
args: [
|
|
855
|
+
args: [accountAddress],
|
|
504
856
|
})) as Hex;
|
|
505
857
|
|
|
506
|
-
// Convert bytes16 hex to UUID string (without dashes)
|
|
507
858
|
const spaceId = spaceIdHex.slice(2, 34).toLowerCase();
|
|
508
|
-
console.log("spaceId", spaceId);
|
|
509
859
|
|
|
510
|
-
|
|
511
|
-
const { ops, id: entityId } = Graph.createEntity({
|
|
860
|
+
const { id: entityId, ops } = Ops.entities.create({
|
|
512
861
|
name: "Test Entity",
|
|
862
|
+
description: "Created via Geo SDK",
|
|
513
863
|
});
|
|
514
|
-
console.log("entityId", entityId);
|
|
515
864
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
name: "Test Edit",
|
|
865
|
+
const tx = await geo.personalSpaces.publishEdit({
|
|
866
|
+
name: "Create Test Entity",
|
|
519
867
|
spaceId,
|
|
520
|
-
ops,
|
|
521
868
|
author: spaceId,
|
|
522
|
-
|
|
869
|
+
ops,
|
|
523
870
|
});
|
|
524
|
-
console.log("cid", cid);
|
|
525
|
-
console.log("editId", editId);
|
|
526
871
|
|
|
527
|
-
// Send transaction via smart account (account and chain are baked in)
|
|
528
872
|
const txHash = await smartAccount.sendTransaction({
|
|
529
|
-
to,
|
|
530
|
-
data: calldata,
|
|
873
|
+
to: tx.to,
|
|
874
|
+
data: tx.calldata,
|
|
531
875
|
});
|
|
532
|
-
console.log("txHash", txHash);
|
|
533
876
|
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
});
|
|
537
|
-
console.log("Successfully published edit to space", spaceId);
|
|
877
|
+
await publicClient.waitForTransactionReceipt({ hash: txHash });
|
|
878
|
+
console.log("Published entity", entityId, "to space", spaceId);
|
|
538
879
|
```
|
|
539
880
|
|
|
540
|
-
## Full
|
|
881
|
+
## Full Personal Space Creation Flow
|
|
541
882
|
|
|
542
|
-
This example
|
|
883
|
+
This example creates a personal space with an EOA wallet and then publishes the initial profile ops returned by `geo.personalSpaces.create(...)`.
|
|
543
884
|
|
|
544
885
|
```ts
|
|
545
|
-
import { createPublicClient, type Hex
|
|
886
|
+
import { createPublicClient, http, type Hex } from "viem";
|
|
546
887
|
import { privateKeyToAccount } from "viem/accounts";
|
|
547
888
|
import {
|
|
548
|
-
|
|
549
|
-
|
|
889
|
+
GeoTestnetConfig,
|
|
890
|
+
createGeoClient,
|
|
550
891
|
getWalletClient,
|
|
551
|
-
TESTNET_RPC_URL,
|
|
552
892
|
} from "@geoprotocol/geo-sdk";
|
|
553
893
|
import { SpaceRegistryAbi } from "@geoprotocol/geo-sdk/abis";
|
|
554
|
-
import { TESTNET } from "@geoprotocol/geo-sdk/contracts";
|
|
555
|
-
|
|
556
|
-
// IMPORTANT: Be careful with your private key. Don't commit it to version control.
|
|
557
|
-
// You can get your private key using https://www.geobrowser.io/export-wallet
|
|
558
|
-
const addressPrivateKey = "0xTODO" as `0x${string}`;
|
|
559
|
-
const { address } = privateKeyToAccount(addressPrivateKey);
|
|
560
894
|
|
|
561
|
-
|
|
895
|
+
const privateKey = "0xTODO" as `0x${string}`;
|
|
896
|
+
const account = privateKeyToAccount(privateKey);
|
|
897
|
+
const geo = createGeoClient({ network: GeoTestnetConfig });
|
|
562
898
|
|
|
563
|
-
// Get wallet client for testnet
|
|
564
899
|
const walletClient = await getWalletClient({
|
|
565
|
-
privateKey
|
|
900
|
+
privateKey,
|
|
566
901
|
});
|
|
567
902
|
|
|
568
|
-
const
|
|
903
|
+
const rpcUrl = GeoTestnetConfig.chain?.rpcUrl;
|
|
904
|
+
const spaceRegistryAddress = GeoTestnetConfig.contracts?.SPACE_REGISTRY_ADDRESS;
|
|
905
|
+
if (!rpcUrl || !spaceRegistryAddress) {
|
|
906
|
+
throw new Error("GeoTestnetConfig is missing registry or RPC configuration.");
|
|
907
|
+
}
|
|
569
908
|
|
|
570
909
|
const publicClient = createPublicClient({
|
|
571
|
-
transport: http(
|
|
910
|
+
transport: http(rpcUrl),
|
|
572
911
|
});
|
|
573
912
|
|
|
574
|
-
|
|
575
|
-
const hasExistingSpace = await personalSpace.hasSpace({
|
|
913
|
+
const hasSpace = await geo.personalSpaces.hasSpace({
|
|
576
914
|
address: account.address,
|
|
577
915
|
});
|
|
578
916
|
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
917
|
+
let spaceIdHex = (await publicClient.readContract({
|
|
918
|
+
address: spaceRegistryAddress,
|
|
919
|
+
abi: SpaceRegistryAbi,
|
|
920
|
+
functionName: "addressToSpaceId",
|
|
921
|
+
args: [account.address],
|
|
922
|
+
})) as Hex;
|
|
923
|
+
|
|
924
|
+
if (!hasSpace) {
|
|
925
|
+
const createSpace = geo.personalSpaces.create({
|
|
926
|
+
name: "Alice",
|
|
927
|
+
accountAddress: account.address,
|
|
928
|
+
});
|
|
929
|
+
|
|
930
|
+
const createSpaceHash = await walletClient.sendTransaction({
|
|
931
|
+
account: walletClient.account,
|
|
932
|
+
to: createSpace.to,
|
|
933
|
+
data: createSpace.calldata,
|
|
934
|
+
});
|
|
582
935
|
|
|
583
|
-
|
|
936
|
+
await publicClient.waitForTransactionReceipt({ hash: createSpaceHash });
|
|
937
|
+
|
|
938
|
+
spaceIdHex = (await publicClient.readContract({
|
|
939
|
+
address: spaceRegistryAddress,
|
|
940
|
+
abi: SpaceRegistryAbi,
|
|
941
|
+
functionName: "addressToSpaceId",
|
|
942
|
+
args: [account.address],
|
|
943
|
+
})) as Hex;
|
|
944
|
+
|
|
945
|
+
const spaceId = spaceIdHex.slice(2, 34).toLowerCase();
|
|
946
|
+
const profileTx = await geo.personalSpaces.publishEdit({
|
|
947
|
+
name: "Create personal space profile",
|
|
948
|
+
spaceId,
|
|
949
|
+
author: spaceId,
|
|
950
|
+
ops: createSpace.ops,
|
|
951
|
+
});
|
|
584
952
|
|
|
585
|
-
const
|
|
953
|
+
const profileHash = await walletClient.sendTransaction({
|
|
586
954
|
account: walletClient.account,
|
|
587
|
-
to,
|
|
588
|
-
data: calldata,
|
|
955
|
+
to: profileTx.to,
|
|
956
|
+
data: profileTx.calldata,
|
|
589
957
|
});
|
|
590
958
|
|
|
591
|
-
await publicClient.waitForTransactionReceipt({ hash:
|
|
592
|
-
}
|
|
959
|
+
await publicClient.waitForTransactionReceipt({ hash: profileHash });
|
|
593
960
|
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
961
|
+
const topicTx = geo.personalSpaces.setTopic({
|
|
962
|
+
spaceId,
|
|
963
|
+
topicId: createSpace.spaceEntityId,
|
|
964
|
+
});
|
|
965
|
+
|
|
966
|
+
const topicHash = await walletClient.sendTransaction({
|
|
967
|
+
account: walletClient.account,
|
|
968
|
+
to: topicTx.to,
|
|
969
|
+
data: topicTx.calldata,
|
|
970
|
+
});
|
|
971
|
+
|
|
972
|
+
await publicClient.waitForTransactionReceipt({ hash: topicHash });
|
|
973
|
+
}
|
|
601
974
|
|
|
602
|
-
// Convert bytes16 hex to UUID string (without dashes)
|
|
603
975
|
const spaceId = spaceIdHex.slice(2, 34).toLowerCase();
|
|
604
|
-
console.log("
|
|
976
|
+
console.log("Personal space ID", spaceId);
|
|
977
|
+
```
|
|
605
978
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
979
|
+
## Utilities
|
|
980
|
+
|
|
981
|
+
### IDs
|
|
982
|
+
|
|
983
|
+
Entities, properties, relations, spaces, and proposals use globally unique IDs. The canonical SDK ID is a UUID v4 without dashes.
|
|
984
|
+
|
|
985
|
+
```ts
|
|
986
|
+
import { Id, IdUtils } from "@geoprotocol/geo-sdk";
|
|
987
|
+
|
|
988
|
+
const generatedId = IdUtils.generate();
|
|
989
|
+
const checkedId = Id(generatedId);
|
|
990
|
+
const bytes = IdUtils.toBytes(checkedId);
|
|
991
|
+
```
|
|
992
|
+
|
|
993
|
+
### Positions
|
|
994
|
+
|
|
995
|
+
`Position` provides fractional indexing helpers for ordered relations and other ordered items.
|
|
996
|
+
|
|
997
|
+
```ts
|
|
998
|
+
import { Position } from "@geoprotocol/geo-sdk";
|
|
999
|
+
|
|
1000
|
+
const first = Position.generate();
|
|
1001
|
+
const between = Position.generateBetween(first, null);
|
|
1002
|
+
const sorted = Position.sort([between, null, first]);
|
|
1003
|
+
```
|
|
1004
|
+
|
|
1005
|
+
### Wallet Helpers
|
|
1006
|
+
|
|
1007
|
+
The Geo Genesis browser uses a smart account associated with your account. You can use the same account from code by exporting your private key from https://www.geobrowser.io/export-wallet.
|
|
1008
|
+
|
|
1009
|
+
```ts
|
|
1010
|
+
import {
|
|
1011
|
+
getSmartAccountWalletClient,
|
|
1012
|
+
getWalletClient,
|
|
1013
|
+
} from "@geoprotocol/geo-sdk";
|
|
1014
|
+
|
|
1015
|
+
const smartAccountWalletClient = await getSmartAccountWalletClient({
|
|
1016
|
+
privateKey,
|
|
1017
|
+
});
|
|
1018
|
+
|
|
1019
|
+
const eoaWalletClient = await getWalletClient({
|
|
1020
|
+
privateKey,
|
|
1021
|
+
});
|
|
1022
|
+
```
|
|
1023
|
+
|
|
1024
|
+
Be careful with private keys. Do not commit them to version control.
|
|
1025
|
+
|
|
1026
|
+
## Legacy API
|
|
1027
|
+
|
|
1028
|
+
The legacy namespaces remain exported for compatibility, but new code should prefer `Ops` and `createGeoClient`.
|
|
1029
|
+
|
|
1030
|
+
| Legacy API | Preferred API |
|
|
1031
|
+
| -------------------------- | ------------------------------------------------------------------------------------ |
|
|
1032
|
+
| `Graph.createEntity(...)` | `Ops.entities.create(...)` |
|
|
1033
|
+
| `Graph.updateEntity(...)` | `Ops.entities.update(...)` |
|
|
1034
|
+
| `Graph.deleteEntity(...)` | `geo.entities.delete(...)` |
|
|
1035
|
+
| `Graph.createImage(...)` | `geo.images.create(...)` |
|
|
1036
|
+
| `Graph.createComment(...)` | `geo.comments.create(...)` or `Ops.comments.create(...)` with supplied reply context |
|
|
1037
|
+
| `Ipfs.publishEdit(...)` | `geo.personalSpaces.publishEdit(...)` or `geo.daoSpaces.proposeEdit(...)` |
|
|
1038
|
+
| `Ipfs.uploadImage(...)` | `geo.storage.uploadImage(...)` |
|
|
1039
|
+
| `Ipfs.uploadCSV(...)` | `geo.storage.uploadCSV(...)` |
|
|
1040
|
+
| `personalSpace.*` | `geo.personalSpaces.*` |
|
|
1041
|
+
| `daoSpace.*` | `geo.daoSpaces.*` |
|
|
1042
|
+
| root encoding helpers | configured client transaction helpers where available |
|
|
1043
|
+
|
|
1044
|
+
### Legacy `Graph`
|
|
1045
|
+
|
|
1046
|
+
```ts
|
|
1047
|
+
import { Graph } from "@geoprotocol/geo-sdk";
|
|
1048
|
+
|
|
1049
|
+
const { id: propertyId, ops: propertyOps } = Graph.createProperty({
|
|
1050
|
+
name: "Website",
|
|
1051
|
+
dataType: "TEXT",
|
|
1052
|
+
});
|
|
1053
|
+
|
|
1054
|
+
const { id: typeId, ops: typeOps } = Graph.createType({
|
|
1055
|
+
name: "Restaurant",
|
|
1056
|
+
properties: [propertyId],
|
|
610
1057
|
});
|
|
611
|
-
console.log("entityId", entityId);
|
|
612
1058
|
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
1059
|
+
const { id: entityId, ops: entityOps } = Graph.createEntity({
|
|
1060
|
+
name: "Yum Yum",
|
|
1061
|
+
types: [typeId],
|
|
1062
|
+
values: [
|
|
1063
|
+
{
|
|
1064
|
+
property: propertyId,
|
|
1065
|
+
type: "text",
|
|
1066
|
+
value: "https://example.com",
|
|
1067
|
+
},
|
|
1068
|
+
],
|
|
1069
|
+
});
|
|
1070
|
+
|
|
1071
|
+
const { ops: updateOps } = Graph.updateEntity({
|
|
1072
|
+
id: entityId,
|
|
1073
|
+
name: "Updated name",
|
|
1074
|
+
});
|
|
1075
|
+
|
|
1076
|
+
const { ops: deleteOps } = await Graph.deleteEntity({
|
|
1077
|
+
id: entityId,
|
|
616
1078
|
spaceId,
|
|
1079
|
+
network: "TESTNET",
|
|
1080
|
+
});
|
|
1081
|
+
```
|
|
1082
|
+
|
|
1083
|
+
Legacy relation helpers:
|
|
1084
|
+
|
|
1085
|
+
```ts
|
|
1086
|
+
const { id: relationId, ops: createRelationOps } = Graph.createRelation({
|
|
1087
|
+
fromEntity: personId,
|
|
1088
|
+
toEntity: restaurantId,
|
|
1089
|
+
type: likesPropertyId,
|
|
1090
|
+
});
|
|
1091
|
+
|
|
1092
|
+
const { ops: updateRelationOps } = Graph.updateRelation({
|
|
1093
|
+
id: relationId,
|
|
1094
|
+
position,
|
|
1095
|
+
});
|
|
1096
|
+
|
|
1097
|
+
const { ops: deleteRelationOps } = Graph.deleteRelation({
|
|
1098
|
+
id: relationId,
|
|
1099
|
+
});
|
|
1100
|
+
```
|
|
1101
|
+
|
|
1102
|
+
Legacy image and comment helpers:
|
|
1103
|
+
|
|
1104
|
+
```ts
|
|
1105
|
+
const image = await Graph.createImage({
|
|
1106
|
+
url: "https://example.com/image.png",
|
|
1107
|
+
network: "TESTNET",
|
|
1108
|
+
});
|
|
1109
|
+
|
|
1110
|
+
const comment = await Graph.createComment({
|
|
1111
|
+
content: "Looks good.",
|
|
1112
|
+
replyTo: { entityId, spaceId },
|
|
1113
|
+
network: "TESTNET",
|
|
1114
|
+
});
|
|
1115
|
+
```
|
|
1116
|
+
|
|
1117
|
+
### Legacy `Ipfs`
|
|
1118
|
+
|
|
1119
|
+
```ts
|
|
1120
|
+
import { Ipfs } from "@geoprotocol/geo-sdk";
|
|
1121
|
+
|
|
1122
|
+
const edit = await Ipfs.publishEdit({
|
|
1123
|
+
name: "Create entity",
|
|
1124
|
+
author: spaceId,
|
|
617
1125
|
ops,
|
|
1126
|
+
network: "TESTNET",
|
|
1127
|
+
});
|
|
1128
|
+
|
|
1129
|
+
const image = await Ipfs.uploadImage(
|
|
1130
|
+
{ url: "https://example.com/image.png" },
|
|
1131
|
+
"TESTNET",
|
|
1132
|
+
);
|
|
1133
|
+
|
|
1134
|
+
const csvCid = await Ipfs.uploadCSV("name,score\nAlice,10", "TESTNET");
|
|
1135
|
+
```
|
|
1136
|
+
|
|
1137
|
+
### Legacy `personalSpace`
|
|
1138
|
+
|
|
1139
|
+
```ts
|
|
1140
|
+
import { personalSpace } from "@geoprotocol/geo-sdk";
|
|
1141
|
+
|
|
1142
|
+
const hasSpace = await personalSpace.hasSpace({
|
|
1143
|
+
address: account.address,
|
|
1144
|
+
});
|
|
1145
|
+
|
|
1146
|
+
const createSpace = personalSpace.createSpace();
|
|
1147
|
+
|
|
1148
|
+
const publish = await personalSpace.publishEdit({
|
|
1149
|
+
name: "My Edit",
|
|
1150
|
+
spaceId,
|
|
618
1151
|
author: spaceId,
|
|
1152
|
+
ops,
|
|
1153
|
+
network: "TESTNET",
|
|
1154
|
+
});
|
|
1155
|
+
```
|
|
1156
|
+
|
|
1157
|
+
### Legacy `daoSpace`
|
|
1158
|
+
|
|
1159
|
+
```ts
|
|
1160
|
+
import { daoSpace } from "@geoprotocol/geo-sdk";
|
|
1161
|
+
|
|
1162
|
+
const createDao = await daoSpace.createSpace({
|
|
1163
|
+
name: "Research DAO",
|
|
1164
|
+
author: authorSpaceId,
|
|
1165
|
+
initialEditorSpaceIds: [authorSpaceId],
|
|
1166
|
+
votingSettings,
|
|
1167
|
+
network: "TESTNET",
|
|
1168
|
+
});
|
|
1169
|
+
|
|
1170
|
+
const proposal = await daoSpace.proposeEdit({
|
|
1171
|
+
name: "Update entity",
|
|
1172
|
+
ops,
|
|
1173
|
+
author: authorSpaceId,
|
|
1174
|
+
daoSpaceAddress,
|
|
1175
|
+
callerSpaceId: authorSpaceId,
|
|
1176
|
+
daoSpaceId,
|
|
1177
|
+
network: "TESTNET",
|
|
1178
|
+
});
|
|
1179
|
+
|
|
1180
|
+
const vote = daoSpace.voteProposal({
|
|
1181
|
+
authorSpaceId,
|
|
1182
|
+
spaceId: daoSpaceId,
|
|
1183
|
+
proposalId,
|
|
1184
|
+
vote: "YES",
|
|
1185
|
+
network: "TESTNET",
|
|
1186
|
+
});
|
|
1187
|
+
|
|
1188
|
+
const execute = daoSpace.executeProposal({
|
|
1189
|
+
authorSpaceId,
|
|
1190
|
+
spaceId: daoSpaceId,
|
|
1191
|
+
proposalId,
|
|
1192
|
+
network: "TESTNET",
|
|
1193
|
+
});
|
|
1194
|
+
```
|
|
1195
|
+
|
|
1196
|
+
Legacy DAO membership helpers:
|
|
1197
|
+
|
|
1198
|
+
```ts
|
|
1199
|
+
daoSpace.proposeAddMember({
|
|
1200
|
+
authorSpaceId,
|
|
1201
|
+
spaceId: daoSpaceId,
|
|
1202
|
+
newMemberSpaceId,
|
|
1203
|
+
network: "TESTNET",
|
|
1204
|
+
});
|
|
1205
|
+
|
|
1206
|
+
daoSpace.proposeRemoveMember({
|
|
1207
|
+
authorSpaceId,
|
|
1208
|
+
spaceId: daoSpaceId,
|
|
1209
|
+
memberToRemoveSpaceId,
|
|
1210
|
+
network: "TESTNET",
|
|
1211
|
+
});
|
|
1212
|
+
|
|
1213
|
+
daoSpace.proposeAddEditor({
|
|
1214
|
+
authorSpaceId,
|
|
1215
|
+
spaceId: daoSpaceId,
|
|
1216
|
+
newEditorSpaceId,
|
|
619
1217
|
network: "TESTNET",
|
|
620
1218
|
});
|
|
621
|
-
console.log("cid", cid);
|
|
622
|
-
console.log("editId", editId);
|
|
623
1219
|
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
1220
|
+
daoSpace.proposeRemoveEditor({
|
|
1221
|
+
authorSpaceId,
|
|
1222
|
+
spaceId: daoSpaceId,
|
|
1223
|
+
editorToRemoveSpaceId,
|
|
1224
|
+
network: "TESTNET",
|
|
629
1225
|
});
|
|
630
|
-
console.log("publishTxHash", publishTxHash);
|
|
631
1226
|
|
|
632
|
-
|
|
633
|
-
|
|
1227
|
+
daoSpace.proposeRequestMembership({
|
|
1228
|
+
authorSpaceId: requesterSpaceId,
|
|
1229
|
+
spaceId: daoSpaceId,
|
|
1230
|
+
network: "TESTNET",
|
|
634
1231
|
});
|
|
635
|
-
|
|
1232
|
+
```
|
|
1233
|
+
|
|
1234
|
+
### Legacy Encoding Helpers
|
|
1235
|
+
|
|
1236
|
+
Low-level encoding helpers are still available for compatibility when you need exact calldata primitives:
|
|
1237
|
+
|
|
1238
|
+
```ts
|
|
1239
|
+
import {
|
|
1240
|
+
getCreateDaoSpaceCalldata,
|
|
1241
|
+
getCreatePersonalSpaceCalldata,
|
|
1242
|
+
getProcessGeoProposalArguments,
|
|
1243
|
+
} from "@geoprotocol/geo-sdk";
|
|
1244
|
+
|
|
1245
|
+
const personalSpaceCalldata = getCreatePersonalSpaceCalldata();
|
|
1246
|
+
|
|
1247
|
+
const daoSpaceCalldata = getCreateDaoSpaceCalldata({
|
|
1248
|
+
votingSettings,
|
|
1249
|
+
initialEditorSpaceIds: [authorSpaceId],
|
|
1250
|
+
initialMemberSpaceIds: [],
|
|
1251
|
+
initialEditsContentUri:
|
|
1252
|
+
"ipfs://bafkreihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku",
|
|
1253
|
+
});
|
|
1254
|
+
|
|
1255
|
+
const proposalArgs = getProcessGeoProposalArguments(
|
|
1256
|
+
spacePluginAddress,
|
|
1257
|
+
"ipfs://bafkreihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku",
|
|
1258
|
+
);
|
|
636
1259
|
```
|